From a4d9a210eb9e6973f591da959b8e0ab02b8e6aaf Mon Sep 17 00:00:00 2001 From: Kefei Mo Date: Thu, 2 May 2024 17:06:10 +0000 Subject: [PATCH 1/2] added VAP quicklook notebooks, added entry point to _toc.yml, setup _config.yml to skip VAP/quicklook/* into build. --- .gitignore | 1 + .../aaf2dsh.c1-checkpoint.ipynb | 1271 +++ .../aaf2dsv.c1-checkpoint.ipynb | 1798 ++++ VAPs/quicklook/2DS-AIR/2DS-AIR_tutorial.ipynb | 838 ++ VAPs/quicklook/2DS-AIR/aaf2dsh.c1.ipynb | 297 + VAPs/quicklook/2DS-AIR/aaf2dsv.c1.ipynb | 297 + .../ACSMCDCE/ACSMCDCE_tutorial.ipynb | 838 ++ VAPs/quicklook/ACSMCDCE/acsmcdce.c1.ipynb | 339 + VAPs/quicklook/ACSMCDCE/acsmcdce.c2.ipynb | 339 + VAPs/quicklook/ACSMCDCE/acsmtofcdce.c1.ipynb | 339 + VAPs/quicklook/AERINF/AERINF_tutorial.ipynb | 838 ++ VAPs/quicklook/AERINF/aerich1nf1turn.c1.ipynb | 297 + VAPs/quicklook/AERINF/aerich2nf1turn.c1.ipynb | 297 + VAPs/quicklook/AERIOE/AERIOE_tutorial.ipynb | 838 ++ VAPs/quicklook/AERIOE/aerioe1turn.c1.ipynb | 297 + .../AERIPROF/AERIPROF_tutorial.ipynb | 838 ++ .../AERIPROF/aeri01prof3feltz.c1.ipynb | 339 + .../AERIPROF/aeriprof3feltz.c1.ipynb | 339 + VAPs/quicklook/AERIPROF/qmeaeriprof.c1.ipynb | 297 + .../AEROSOLBE/AEROSOLBE_tutorial.ipynb | 838 ++ .../AEROSOLBE/aerosolbe1turn.c1.ipynb | 339 + VAPs/quicklook/AIP/AIP_tutorial.ipynb | 838 ++ VAPs/quicklook/AIP/aip1ogren.c1.ipynb | 339 + VAPs/quicklook/AIP/aipavg1ogren.c1.ipynb | 339 + VAPs/quicklook/AIP/aipfitrh1ogren.c1.ipynb | 339 + .../AOD-MFRSR/AOD-MFRSR_tutorial.ipynb | 838 ++ .../AOD-MFRSR/mfrsr7nchaod1mich.c1.ipynb | 339 + .../quicklook/AOD-MFRSR/mfrsr7nchcal.c1.ipynb | 339 + .../AOD-MFRSR/mfrsraod1mich.c1.ipynb | 339 + VAPs/quicklook/AOD-MFRSR/mfrsrcal.c1.ipynb | 339 + .../AOD-NIMFR/AOD-NIMFR_tutorial.ipynb | 838 ++ .../AOD-NIMFR/nimfraod1mich.c1.ipynb | 339 + VAPs/quicklook/AOD/AOD_tutorial.ipynb | 838 ++ VAPs/quicklook/AOD/sasheniraod.c1.ipynb | 339 + VAPs/quicklook/AOD/sashevisaod.c1.ipynb | 339 + .../aoppsap1flynn1m.c1-checkpoint.ipynb | 8265 +++++++++++++++++ VAPs/quicklook/AOP/AOP_tutorial.ipynb | 838 ++ VAPs/quicklook/AOP/aopclap1flynn1m.c1.ipynb | 339 + VAPs/quicklook/AOP/aoppsap1flynn1h.c1.ipynb | 339 + VAPs/quicklook/AOP/aoppsap1flynn1m.c1.ipynb | 339 + .../AOSCCNAVG/AOSCCNAVG_tutorial.ipynb | 838 ++ VAPs/quicklook/AOSCCNAVG/aosccnavg.c1.ipynb | 339 + VAPs/quicklook/AOSCCNAVG/aosccnavg.c2.ipynb | 339 + .../AOSSP2BC/AOSSP2BC_tutorial.ipynb | 838 ++ VAPs/quicklook/AOSSP2BC/aossp2rbc1m.c1.ipynb | 339 + VAPs/quicklook/ARMBE/ARMBE_tutorial.ipynb | 838 ++ VAPs/quicklook/ARMBE/armbeatm.c1.ipynb | 297 + VAPs/quicklook/ARMBE/armbecldrad.c1.ipynb | 339 + .../arscl1cloth.c1-checkpoint.ipynb | 2631 ++++++ .../arsclbnd1cloth.c1-checkpoint.ipynb | 1937 ++++ VAPs/quicklook/ARSCL/ARSCL_tutorial.ipynb | 838 ++ VAPs/quicklook/ARSCL/arscl1cloth.c1.ipynb | 339 + VAPs/quicklook/ARSCL/arsclbnd1cloth.c1.ipynb | 339 + .../ASDBE-AIR/ASDBE-AIR_tutorial.ipynb | 838 ++ .../ASDBE-AIR/aafmergedaerosolsd.c1.ipynb | 297 + VAPs/quicklook/BAEBBR/30baebbr.c1.ipynb | 339 + VAPs/quicklook/BAEBBR/BAEBBR_tutorial.ipynb | 838 ++ .../bbhrpavg1mlawer.c1-checkpoint.ipynb | 3768 ++++++++ .../BBHRP/1bbhrpripbe1mcfarlane.c1.ipynb | 339 + .../BBHRP/30bbhrpripbe1mcfarlane.c1.ipynb | 339 + VAPs/quicklook/BBHRP/BBHRP_tutorial.ipynb | 838 ++ VAPs/quicklook/BBHRP/bbhrpavg1mlawer.c1.ipynb | 339 + VAPs/quicklook/BEFLUX/BEFLUX_tutorial.ipynb | 838 ++ VAPs/quicklook/BEFLUX/beflux1long.c1.ipynb | 297 + VAPs/quicklook/BEFLUX/qcflux1long.c1.ipynb | 297 + .../CCNKAPPA/CCNKAPPA_tutorial.ipynb | 838 ++ .../CCNKAPPA/aosccnsmpskappa.c1.ipynb | 339 + .../rlccnprof1ghan.c1-checkpoint.ipynb | 4109 ++++++++ VAPs/quicklook/CCNPROF/CCNPROF_tutorial.ipynb | 838 ++ .../quicklook/CCNPROF/rlccnprof1ghan.c1.ipynb | 339 + VAPs/quicklook/CLAP/CLAP_tutorial.ipynb | 838 ++ VAPs/quicklook/CLAP/aosclap3w.c1.ipynb | 339 + VAPs/quicklook/CLDTYPE/CLDTYPE_tutorial.ipynb | 838 ++ VAPs/quicklook/CLDTYPE/cldtype.c1.ipynb | 339 + .../cmac2.c1-checkpoint.ipynb | 3537 +++++++ VAPs/quicklook/CMAC2/CMAC2_tutorial.ipynb | 838 ++ VAPs/quicklook/CMAC2/cmac2.c1.ipynb | 297 + VAPs/quicklook/CO-AIR/CO-AIR_tutorial.ipynb | 838 ++ VAPs/quicklook/CO-AIR/aafco.c1.ipynb | 297 + VAPs/quicklook/COGS/COGS_tutorial.ipynb | 838 ++ VAPs/quicklook/COGS/cogs.c1.ipynb | 297 + VAPs/quicklook/DIFFCOR/DIFFCOR_tutorial.ipynb | 838 ++ VAPs/quicklook/DIFFCOR/brs1dutt.c1.ipynb | 297 + VAPs/quicklook/DIFFCOR/siros1dutt.c1.ipynb | 297 + VAPs/quicklook/DIFFCOR/sirs1dutt.c1.ipynb | 297 + .../DLPROF-WIND/DLPROF-WIND_tutorial.ipynb | 838 ++ .../DLPROF-WIND/dlprofwind4news.c1.ipynb | 297 + .../DLPROF-WSTATS_tutorial.ipynb | 838 ++ .../DLPROF-WSTATS/dlprofwstats4news.c1.ipynb | 297 + .../FCDP-AIR/FCDP-AIR_tutorial.ipynb | 838 ++ VAPs/quicklook/FCDP-AIR/aaffcdp.c1.ipynb | 297 + VAPs/quicklook/GVR/GVR_tutorial.ipynb | 838 ++ VAPs/quicklook/GVR/gvr.c1.ipynb | 339 + .../HVPS-AIR/HVPS-AIR_tutorial.ipynb | 838 ++ VAPs/quicklook/HVPS-AIR/aafhvps.c1.ipynb | 297 + .../INLETCVI-AIR/INLETCVI-AIR_tutorial.ipynb | 838 ++ .../INLETCVI-AIR/aafinletcvi.c1.ipynb | 339 + .../INTERPSONDE/INTERPSONDE_tutorial.ipynb | 838 ++ .../INTERPSONDE/interpolatedsonde.c1.ipynb | 339 + .../KAZRARSCL/KAZRARSCL_tutorial.ipynb | 838 ++ .../KAZRARSCL/arsclkazr1kollias.c1.ipynb | 339 + .../KAZRARSCL/arsclkazrbnd1kollias.c1.ipynb | 297 + .../KAZRARSCLCLOUDSAT_tutorial.ipynb | 838 ++ .../arsclkazrcloudsat.c1.ipynb | 339 + .../KAZRCFRCOR/KAZRCFRCOR_tutorial.ipynb | 838 ++ .../KAZRCFRCOR/kazrcfrcorge.c1.ipynb | 339 + .../KAZRCFRCOR/kazrcfrcormd.c1.ipynb | 339 + .../kazrcorge.c1-checkpoint.ipynb | 445 + .../kazrcorhi.c1-checkpoint.ipynb | 1856 ++++ .../kazrcormd.c1-checkpoint.ipynb | 2667 ++++++ VAPs/quicklook/KAZRCOR/KAZRCOR_tutorial.ipynb | 838 ++ VAPs/quicklook/KAZRCOR/kazrcorge.c1.ipynb | 339 + VAPs/quicklook/KAZRCOR/kazrcorhi.c1.ipynb | 339 + VAPs/quicklook/KAZRCOR/kazrcormd.c1.ipynb | 339 + .../LCLHEIGHT/LCLHEIGHT_tutorial.ipynb | 838 ++ VAPs/quicklook/LCLHEIGHT/lcl.c1.ipynb | 339 + .../LDQUANTS/LDQUANTS_tutorial.ipynb | 838 ++ VAPs/quicklook/LDQUANTS/ldquants.c1.ipynb | 297 + VAPs/quicklook/LSSONDE/LSSONDE_tutorial.ipynb | 838 ++ VAPs/quicklook/LSSONDE/lssonde.c1.ipynb | 297 + .../MASCPARTICLES_tutorial.ipynb | 838 ++ .../MASCPARTICLES/mascparticles.c1.ipynb | 339 + .../MASCPARTICLES/mascparticlesavg.c1.ipynb | 339 + .../MERGED-COMMON_tutorial.ipynb | 838 ++ .../MERGED-COMMON/aafmergedcldsd.c1.ipynb | 297 + .../MERGEDSMPSAPS_tutorial.ipynb | 838 ++ .../MERGEDSMPSAPS/mergedsmpsaps.c1.ipynb | 339 + .../MERGESONDE/MERGESONDE_tutorial.ipynb | 838 ++ .../MERGESONDE/mergesonde1mace.c1.ipynb | 339 + .../MERGESONDE/mergesonde2mace.c1.ipynb | 339 + .../mfrsrcldod1min.c1-checkpoint.ipynb | 799 ++ .../MFRSRCLDOD/MFRSRCLDOD_tutorial.ipynb | 838 ++ .../MFRSRCLDOD/mfrsrcldod1min.c1.ipynb | 339 + .../microbasepi2.c1-checkpoint.ipynb | 468 + .../microbasepiavg.c1-checkpoint.ipynb | 1757 ++++ .../MICROBASE/MICROBASE_tutorial.ipynb | 838 ++ VAPs/quicklook/MICROBASE/microbasepi.c1.ipynb | 297 + .../quicklook/MICROBASE/microbasepi2.c1.ipynb | 297 + .../MICROBASE/microbasepiavg.c1.ipynb | 297 + VAPs/quicklook/MPLAVG/MPLAVG_tutorial.ipynb | 838 ++ VAPs/quicklook/MPLAVG/mplpolavg.c1.ipynb | 339 + .../MPLCMASK/30smplcmask1zwang.c1.ipynb | 368 + .../MPLCMASK/MPLCMASK_tutorial.ipynb | 867 ++ .../MPLCMASKML/MPLCMASKML_tutorial.ipynb | 838 ++ VAPs/quicklook/MPLCMASKML/mplcmaskml.c1.ipynb | 339 + .../mplnor1camp.c1-checkpoint.ipynb | 1732 ++++ VAPs/quicklook/MPLNOR/MPLNOR_tutorial.ipynb | 838 ++ VAPs/quicklook/MPLNOR/mplnor1camp.c1.ipynb | 297 + VAPs/quicklook/MWRRET/MWRRET_tutorial.ipynb | 838 ++ .../quicklook/MWRRET/mwrret1liljclou.c1.ipynb | 339 + .../quicklook/MWRRET/mwrret1liljclou.c2.ipynb | 339 + .../MWRRETV2/MWRRETV2_tutorial.ipynb | 838 ++ VAPs/quicklook/MWRRETV2/mwrret2turn.c1.ipynb | 339 + .../NAVMET-AIR/NAVMET-AIR_tutorial.ipynb | 838 ++ VAPs/quicklook/NAVMET-AIR/aafnaviwg.c1.ipynb | 297 + VAPs/quicklook/NDROP/NDROP_tutorial.ipynb | 838 ++ VAPs/quicklook/NDROP/ndropmfrsr.c1.ipynb | 339 + .../NEPHELOMETER/NEPHELOMETER_tutorial.ipynb | 838 ++ .../NEPHELOMETER/aosnephdry.c1.ipynb | 339 + .../NEPHELOMETER/aosnephwet.c1.ipynb | 339 + .../okmsoil.c1-checkpoint.ipynb | 2048 ++++ VAPs/quicklook/OKMSOIL/OKMSOIL_tutorial.ipynb | 838 ++ VAPs/quicklook/OKMSOIL/okmsoil.c1.ipynb | 339 + .../OZONE-AIR/OZONE-AIR_tutorial.ipynb | 838 ++ VAPs/quicklook/OZONE-AIR/aafo3.c1.ipynb | 297 + .../pblhtsonde1mcfarl.c1-checkpoint.ipynb | 679 ++ VAPs/quicklook/PBLHT/PBLHT_tutorial.ipynb | 838 ++ .../PBLHT/pblhtsonde1mcfarl.c1.ipynb | 297 + .../PBLHT/pblhtsondeyr1mcfarl.c1.ipynb | 339 + VAPs/quicklook/PCCP/PCCP_tutorial.ipynb | 943 ++ VAPs/quicklook/PCCP/pccp.c1.ipynb | 310 + .../aospsap3w.c1-checkpoint.ipynb | 1841 ++++ VAPs/quicklook/PSAP/PSAP_tutorial.ipynb | 838 ++ VAPs/quicklook/PSAP/aospsap3w.c1.ipynb | 339 + VAPs/quicklook/QCRAD/QCRAD_tutorial.ipynb | 838 ++ VAPs/quicklook/QCRAD/qcrad1long.c1.ipynb | 339 + VAPs/quicklook/QCRAD/qcrad1long.c2.ipynb | 339 + .../quicklook/QCRAD/qcradbeflux1long.c1.ipynb | 339 + .../quicklook/QCRAD/qcradbeflux1long.c2.ipynb | 339 + VAPs/quicklook/QCRAD/qcradbrs1long.c1.ipynb | 339 + VAPs/quicklook/QCRAD/qcradbrs1long.c2.ipynb | 339 + .../radflux1long.c1-checkpoint.ipynb | 3763 ++++++++ .../RADFLUXANAL/RADFLUXANAL_tutorial.ipynb | 838 ++ .../RADFLUXANAL/radflux1long.c1.ipynb | 339 + .../RADFLUXANAL/radflux1long.c2.ipynb | 339 + .../RADFLUXANAL/radfluxbrs1long.c2.ipynb | 339 + .../RIPBE/30ripbe1mcfarlane.c1.ipynb | 339 + VAPs/quicklook/RIPBE/RIPBE_tutorial.ipynb | 838 ++ VAPs/quicklook/RIPBE/ripbe1mcfarlane.c1.ipynb | 339 + .../quicklook/RLPROF/10rlprofbe1news.c1.ipynb | 339 + VAPs/quicklook/RLPROF/RLPROF_tutorial.ipynb | 838 ++ .../kasacradv3d3c.c1-checkpoint.ipynb | 2574 +++++ .../SACRADV3D3C/SACRADV3D3C_tutorial.ipynb | 838 ++ .../SACRADV3D3C/kasacradv3d3c.c1.ipynb | 297 + .../SACRADVVAD/SACRADVVAD_tutorial.ipynb | 838 ++ .../SACRADVVAD/kasacradvvad.c1.ipynb | 297 + .../15swfcldgrid1long.c1-checkpoint.ipynb | 2384 +++++ ...fccldgrid2longcaracena.c1-checkpoint.ipynb | 5150 ++++++++++ .../SFCCLDGRID/15swfcldgrid1long.c1.ipynb | 339 + .../SFCCLDGRID/SFCCLDGRID_tutorial.ipynb | 838 ++ .../sfccldgrid2longcaracena.c1.ipynb | 339 + .../sfccldgrid2longstation.c1.ipynb | 339 + .../SHALLOWCUMULUS_tutorial.ipynb | 838 ++ .../SHALLOWCUMULUS/shallowcumulus.c1.ipynb | 297 + .../SHALLOWCUMULUS/shcusummary.c1.ipynb | 297 + VAPs/quicklook/SO2-AIR/SO2-AIR_tutorial.ipynb | 838 ++ VAPs/quicklook/SO2-AIR/aafso2.c1.ipynb | 297 + .../SONDEADJUST_tutorial-checkpoint.ipynb | 4058 ++++++++ .../SONDEADJUST/SONDEADJUST_tutorial.ipynb | 884 ++ .../SONDEADJUST/sondeadjust.c1.ipynb | 385 + .../SONDEPARAM/SONDEPARAM_tutorial.ipynb | 838 ++ VAPs/quicklook/SONDEPARAM/sondeparam.c1.ipynb | 297 + VAPs/quicklook/SP2-AIR/SP2-AIR_tutorial.ipynb | 838 ++ VAPs/quicklook/SP2-AIR/aafsp2rbc10s.c1.ipynb | 339 + .../SPHOTCOD/SPHOTCOD_tutorial.ipynb | 838 ++ .../quicklook/SPHOTCOD/sphotcod2chiu.c1.ipynb | 297 + .../SURFSPECALB/SURFSPECALB_tutorial.ipynb | 838 ++ .../SURFSPECALB/surfspecalb1mlawer.c1.ipynb | 339 + .../surfspecalb7nch1mlawer.c1.ipynb | 339 + .../1swfanalsirs1long.c1-checkpoint.ipynb | 2654 ++++++ .../TBSMERGED/TBSMERGED_tutorial.ipynb | 838 ++ VAPs/quicklook/TBSMERGED/tbsmerged.c1.ipynb | 339 + .../TBSMERGED/tbsmergedincloud.c1.ipynb | 339 + VAPs/quicklook/TDMA/TDMA_tutorial.ipynb | 838 ++ VAPs/quicklook/TDMA/tdmaapssize.c1.ipynb | 339 + VAPs/quicklook/TWRMR/1twrmr.c1.ipynb | 339 + VAPs/quicklook/TWRMR/30twrmr.c1.ipynb | 297 + VAPs/quicklook/TWRMR/TWRMR_tutorial.ipynb | 838 ++ .../quicklook/VARANAL/180varanaecmwf.c1.ipynb | 297 + .../VARANAL/180varanamerra001.c1.ipynb | 297 + VAPs/quicklook/VARANAL/VARANAL_tutorial.ipynb | 838 ++ .../VARANAL3D/180varanal3dera5.c1.ipynb | 297 + .../VARANAL3D/180varanal3dncep.c1.ipynb | 297 + .../VARANAL3D/VARANAL3D_tutorial.ipynb | 838 ++ .../VDISQUANTS/VDISQUANTS_tutorial.ipynb | 838 ++ VAPs/quicklook/VDISQUANTS/vdisquants.c1.ipynb | 297 + .../WACRARSCL/WACRARSCL_tutorial.ipynb | 838 ++ .../WACRARSCL/arsclwacr1kollias.c1.ipynb | 339 + .../WACRARSCL/arsclwacrbnd1kollias.c1.ipynb | 339 + VAPs/vap_notebook_list.md | 1010 ++ _config.yml | 1 + _toc.yml | 1 + 242 files changed, 175046 insertions(+) create mode 100644 VAPs/quicklook/2DS-AIR/.ipynb_checkpoints/aaf2dsh.c1-checkpoint.ipynb create mode 100644 VAPs/quicklook/2DS-AIR/.ipynb_checkpoints/aaf2dsv.c1-checkpoint.ipynb create mode 100644 VAPs/quicklook/2DS-AIR/2DS-AIR_tutorial.ipynb create mode 100644 VAPs/quicklook/2DS-AIR/aaf2dsh.c1.ipynb create mode 100644 VAPs/quicklook/2DS-AIR/aaf2dsv.c1.ipynb create mode 100644 VAPs/quicklook/ACSMCDCE/ACSMCDCE_tutorial.ipynb create mode 100644 VAPs/quicklook/ACSMCDCE/acsmcdce.c1.ipynb create mode 100644 VAPs/quicklook/ACSMCDCE/acsmcdce.c2.ipynb create mode 100644 VAPs/quicklook/ACSMCDCE/acsmtofcdce.c1.ipynb create mode 100644 VAPs/quicklook/AERINF/AERINF_tutorial.ipynb create mode 100644 VAPs/quicklook/AERINF/aerich1nf1turn.c1.ipynb create mode 100644 VAPs/quicklook/AERINF/aerich2nf1turn.c1.ipynb create mode 100644 VAPs/quicklook/AERIOE/AERIOE_tutorial.ipynb create mode 100644 VAPs/quicklook/AERIOE/aerioe1turn.c1.ipynb create mode 100644 VAPs/quicklook/AERIPROF/AERIPROF_tutorial.ipynb create mode 100644 VAPs/quicklook/AERIPROF/aeri01prof3feltz.c1.ipynb create mode 100644 VAPs/quicklook/AERIPROF/aeriprof3feltz.c1.ipynb create mode 100644 VAPs/quicklook/AERIPROF/qmeaeriprof.c1.ipynb create mode 100644 VAPs/quicklook/AEROSOLBE/AEROSOLBE_tutorial.ipynb create mode 100644 VAPs/quicklook/AEROSOLBE/aerosolbe1turn.c1.ipynb create mode 100644 VAPs/quicklook/AIP/AIP_tutorial.ipynb create mode 100644 VAPs/quicklook/AIP/aip1ogren.c1.ipynb create mode 100644 VAPs/quicklook/AIP/aipavg1ogren.c1.ipynb create mode 100644 VAPs/quicklook/AIP/aipfitrh1ogren.c1.ipynb create mode 100644 VAPs/quicklook/AOD-MFRSR/AOD-MFRSR_tutorial.ipynb create mode 100644 VAPs/quicklook/AOD-MFRSR/mfrsr7nchaod1mich.c1.ipynb create mode 100644 VAPs/quicklook/AOD-MFRSR/mfrsr7nchcal.c1.ipynb create mode 100644 VAPs/quicklook/AOD-MFRSR/mfrsraod1mich.c1.ipynb create mode 100644 VAPs/quicklook/AOD-MFRSR/mfrsrcal.c1.ipynb create mode 100644 VAPs/quicklook/AOD-NIMFR/AOD-NIMFR_tutorial.ipynb create mode 100644 VAPs/quicklook/AOD-NIMFR/nimfraod1mich.c1.ipynb create mode 100644 VAPs/quicklook/AOD/AOD_tutorial.ipynb create mode 100644 VAPs/quicklook/AOD/sasheniraod.c1.ipynb create mode 100644 VAPs/quicklook/AOD/sashevisaod.c1.ipynb create mode 100644 VAPs/quicklook/AOP/.ipynb_checkpoints/aoppsap1flynn1m.c1-checkpoint.ipynb create mode 100644 VAPs/quicklook/AOP/AOP_tutorial.ipynb create mode 100644 VAPs/quicklook/AOP/aopclap1flynn1m.c1.ipynb create mode 100644 VAPs/quicklook/AOP/aoppsap1flynn1h.c1.ipynb create mode 100644 VAPs/quicklook/AOP/aoppsap1flynn1m.c1.ipynb create mode 100644 VAPs/quicklook/AOSCCNAVG/AOSCCNAVG_tutorial.ipynb create mode 100644 VAPs/quicklook/AOSCCNAVG/aosccnavg.c1.ipynb create mode 100644 VAPs/quicklook/AOSCCNAVG/aosccnavg.c2.ipynb create mode 100644 VAPs/quicklook/AOSSP2BC/AOSSP2BC_tutorial.ipynb create mode 100644 VAPs/quicklook/AOSSP2BC/aossp2rbc1m.c1.ipynb create mode 100644 VAPs/quicklook/ARMBE/ARMBE_tutorial.ipynb create mode 100644 VAPs/quicklook/ARMBE/armbeatm.c1.ipynb create mode 100644 VAPs/quicklook/ARMBE/armbecldrad.c1.ipynb create mode 100644 VAPs/quicklook/ARSCL/.ipynb_checkpoints/arscl1cloth.c1-checkpoint.ipynb create mode 100644 VAPs/quicklook/ARSCL/.ipynb_checkpoints/arsclbnd1cloth.c1-checkpoint.ipynb create mode 100644 VAPs/quicklook/ARSCL/ARSCL_tutorial.ipynb create mode 100644 VAPs/quicklook/ARSCL/arscl1cloth.c1.ipynb create mode 100644 VAPs/quicklook/ARSCL/arsclbnd1cloth.c1.ipynb create mode 100644 VAPs/quicklook/ASDBE-AIR/ASDBE-AIR_tutorial.ipynb create mode 100644 VAPs/quicklook/ASDBE-AIR/aafmergedaerosolsd.c1.ipynb create mode 100644 VAPs/quicklook/BAEBBR/30baebbr.c1.ipynb create mode 100644 VAPs/quicklook/BAEBBR/BAEBBR_tutorial.ipynb create mode 100644 VAPs/quicklook/BBHRP/.ipynb_checkpoints/bbhrpavg1mlawer.c1-checkpoint.ipynb create mode 100644 VAPs/quicklook/BBHRP/1bbhrpripbe1mcfarlane.c1.ipynb create mode 100644 VAPs/quicklook/BBHRP/30bbhrpripbe1mcfarlane.c1.ipynb create mode 100644 VAPs/quicklook/BBHRP/BBHRP_tutorial.ipynb create mode 100644 VAPs/quicklook/BBHRP/bbhrpavg1mlawer.c1.ipynb create mode 100644 VAPs/quicklook/BEFLUX/BEFLUX_tutorial.ipynb create mode 100644 VAPs/quicklook/BEFLUX/beflux1long.c1.ipynb create mode 100644 VAPs/quicklook/BEFLUX/qcflux1long.c1.ipynb create mode 100644 VAPs/quicklook/CCNKAPPA/CCNKAPPA_tutorial.ipynb create mode 100644 VAPs/quicklook/CCNKAPPA/aosccnsmpskappa.c1.ipynb create mode 100644 VAPs/quicklook/CCNPROF/.ipynb_checkpoints/rlccnprof1ghan.c1-checkpoint.ipynb create mode 100644 VAPs/quicklook/CCNPROF/CCNPROF_tutorial.ipynb create mode 100644 VAPs/quicklook/CCNPROF/rlccnprof1ghan.c1.ipynb create mode 100644 VAPs/quicklook/CLAP/CLAP_tutorial.ipynb create mode 100644 VAPs/quicklook/CLAP/aosclap3w.c1.ipynb create mode 100644 VAPs/quicklook/CLDTYPE/CLDTYPE_tutorial.ipynb create mode 100644 VAPs/quicklook/CLDTYPE/cldtype.c1.ipynb create mode 100644 VAPs/quicklook/CMAC2/.ipynb_checkpoints/cmac2.c1-checkpoint.ipynb create mode 100644 VAPs/quicklook/CMAC2/CMAC2_tutorial.ipynb create mode 100644 VAPs/quicklook/CMAC2/cmac2.c1.ipynb create mode 100644 VAPs/quicklook/CO-AIR/CO-AIR_tutorial.ipynb create mode 100644 VAPs/quicklook/CO-AIR/aafco.c1.ipynb create mode 100644 VAPs/quicklook/COGS/COGS_tutorial.ipynb create mode 100644 VAPs/quicklook/COGS/cogs.c1.ipynb create mode 100644 VAPs/quicklook/DIFFCOR/DIFFCOR_tutorial.ipynb create mode 100644 VAPs/quicklook/DIFFCOR/brs1dutt.c1.ipynb create mode 100644 VAPs/quicklook/DIFFCOR/siros1dutt.c1.ipynb create mode 100644 VAPs/quicklook/DIFFCOR/sirs1dutt.c1.ipynb create mode 100644 VAPs/quicklook/DLPROF-WIND/DLPROF-WIND_tutorial.ipynb create mode 100644 VAPs/quicklook/DLPROF-WIND/dlprofwind4news.c1.ipynb create mode 100644 VAPs/quicklook/DLPROF-WSTATS/DLPROF-WSTATS_tutorial.ipynb create mode 100644 VAPs/quicklook/DLPROF-WSTATS/dlprofwstats4news.c1.ipynb create mode 100644 VAPs/quicklook/FCDP-AIR/FCDP-AIR_tutorial.ipynb create mode 100644 VAPs/quicklook/FCDP-AIR/aaffcdp.c1.ipynb create mode 100644 VAPs/quicklook/GVR/GVR_tutorial.ipynb create mode 100644 VAPs/quicklook/GVR/gvr.c1.ipynb create mode 100644 VAPs/quicklook/HVPS-AIR/HVPS-AIR_tutorial.ipynb create mode 100644 VAPs/quicklook/HVPS-AIR/aafhvps.c1.ipynb create mode 100644 VAPs/quicklook/INLETCVI-AIR/INLETCVI-AIR_tutorial.ipynb create mode 100644 VAPs/quicklook/INLETCVI-AIR/aafinletcvi.c1.ipynb create mode 100644 VAPs/quicklook/INTERPSONDE/INTERPSONDE_tutorial.ipynb create mode 100644 VAPs/quicklook/INTERPSONDE/interpolatedsonde.c1.ipynb create mode 100644 VAPs/quicklook/KAZRARSCL/KAZRARSCL_tutorial.ipynb create mode 100644 VAPs/quicklook/KAZRARSCL/arsclkazr1kollias.c1.ipynb create mode 100644 VAPs/quicklook/KAZRARSCL/arsclkazrbnd1kollias.c1.ipynb create mode 100644 VAPs/quicklook/KAZRARSCLCLOUDSAT/KAZRARSCLCLOUDSAT_tutorial.ipynb create mode 100644 VAPs/quicklook/KAZRARSCLCLOUDSAT/arsclkazrcloudsat.c1.ipynb create mode 100644 VAPs/quicklook/KAZRCFRCOR/KAZRCFRCOR_tutorial.ipynb create mode 100644 VAPs/quicklook/KAZRCFRCOR/kazrcfrcorge.c1.ipynb create mode 100644 VAPs/quicklook/KAZRCFRCOR/kazrcfrcormd.c1.ipynb create mode 100644 VAPs/quicklook/KAZRCOR/.ipynb_checkpoints/kazrcorge.c1-checkpoint.ipynb create mode 100644 VAPs/quicklook/KAZRCOR/.ipynb_checkpoints/kazrcorhi.c1-checkpoint.ipynb create mode 100644 VAPs/quicklook/KAZRCOR/.ipynb_checkpoints/kazrcormd.c1-checkpoint.ipynb create mode 100644 VAPs/quicklook/KAZRCOR/KAZRCOR_tutorial.ipynb create mode 100644 VAPs/quicklook/KAZRCOR/kazrcorge.c1.ipynb create mode 100644 VAPs/quicklook/KAZRCOR/kazrcorhi.c1.ipynb create mode 100644 VAPs/quicklook/KAZRCOR/kazrcormd.c1.ipynb create mode 100644 VAPs/quicklook/LCLHEIGHT/LCLHEIGHT_tutorial.ipynb create mode 100644 VAPs/quicklook/LCLHEIGHT/lcl.c1.ipynb create mode 100644 VAPs/quicklook/LDQUANTS/LDQUANTS_tutorial.ipynb create mode 100644 VAPs/quicklook/LDQUANTS/ldquants.c1.ipynb create mode 100644 VAPs/quicklook/LSSONDE/LSSONDE_tutorial.ipynb create mode 100644 VAPs/quicklook/LSSONDE/lssonde.c1.ipynb create mode 100644 VAPs/quicklook/MASCPARTICLES/MASCPARTICLES_tutorial.ipynb create mode 100644 VAPs/quicklook/MASCPARTICLES/mascparticles.c1.ipynb create mode 100644 VAPs/quicklook/MASCPARTICLES/mascparticlesavg.c1.ipynb create mode 100644 VAPs/quicklook/MERGED-COMMON/MERGED-COMMON_tutorial.ipynb create mode 100644 VAPs/quicklook/MERGED-COMMON/aafmergedcldsd.c1.ipynb create mode 100644 VAPs/quicklook/MERGEDSMPSAPS/MERGEDSMPSAPS_tutorial.ipynb create mode 100644 VAPs/quicklook/MERGEDSMPSAPS/mergedsmpsaps.c1.ipynb create mode 100644 VAPs/quicklook/MERGESONDE/MERGESONDE_tutorial.ipynb create mode 100644 VAPs/quicklook/MERGESONDE/mergesonde1mace.c1.ipynb create mode 100644 VAPs/quicklook/MERGESONDE/mergesonde2mace.c1.ipynb create mode 100644 VAPs/quicklook/MFRSRCLDOD/.ipynb_checkpoints/mfrsrcldod1min.c1-checkpoint.ipynb create mode 100644 VAPs/quicklook/MFRSRCLDOD/MFRSRCLDOD_tutorial.ipynb create mode 100644 VAPs/quicklook/MFRSRCLDOD/mfrsrcldod1min.c1.ipynb create mode 100644 VAPs/quicklook/MICROBASE/.ipynb_checkpoints/microbasepi2.c1-checkpoint.ipynb create mode 100644 VAPs/quicklook/MICROBASE/.ipynb_checkpoints/microbasepiavg.c1-checkpoint.ipynb create mode 100644 VAPs/quicklook/MICROBASE/MICROBASE_tutorial.ipynb create mode 100644 VAPs/quicklook/MICROBASE/microbasepi.c1.ipynb create mode 100644 VAPs/quicklook/MICROBASE/microbasepi2.c1.ipynb create mode 100644 VAPs/quicklook/MICROBASE/microbasepiavg.c1.ipynb create mode 100644 VAPs/quicklook/MPLAVG/MPLAVG_tutorial.ipynb create mode 100644 VAPs/quicklook/MPLAVG/mplpolavg.c1.ipynb create mode 100644 VAPs/quicklook/MPLCMASK/30smplcmask1zwang.c1.ipynb create mode 100644 VAPs/quicklook/MPLCMASK/MPLCMASK_tutorial.ipynb create mode 100644 VAPs/quicklook/MPLCMASKML/MPLCMASKML_tutorial.ipynb create mode 100644 VAPs/quicklook/MPLCMASKML/mplcmaskml.c1.ipynb create mode 100644 VAPs/quicklook/MPLNOR/.ipynb_checkpoints/mplnor1camp.c1-checkpoint.ipynb create mode 100644 VAPs/quicklook/MPLNOR/MPLNOR_tutorial.ipynb create mode 100644 VAPs/quicklook/MPLNOR/mplnor1camp.c1.ipynb create mode 100644 VAPs/quicklook/MWRRET/MWRRET_tutorial.ipynb create mode 100644 VAPs/quicklook/MWRRET/mwrret1liljclou.c1.ipynb create mode 100644 VAPs/quicklook/MWRRET/mwrret1liljclou.c2.ipynb create mode 100644 VAPs/quicklook/MWRRETV2/MWRRETV2_tutorial.ipynb create mode 100644 VAPs/quicklook/MWRRETV2/mwrret2turn.c1.ipynb create mode 100644 VAPs/quicklook/NAVMET-AIR/NAVMET-AIR_tutorial.ipynb create mode 100644 VAPs/quicklook/NAVMET-AIR/aafnaviwg.c1.ipynb create mode 100644 VAPs/quicklook/NDROP/NDROP_tutorial.ipynb create mode 100644 VAPs/quicklook/NDROP/ndropmfrsr.c1.ipynb create mode 100644 VAPs/quicklook/NEPHELOMETER/NEPHELOMETER_tutorial.ipynb create mode 100644 VAPs/quicklook/NEPHELOMETER/aosnephdry.c1.ipynb create mode 100644 VAPs/quicklook/NEPHELOMETER/aosnephwet.c1.ipynb create mode 100644 VAPs/quicklook/OKMSOIL/.ipynb_checkpoints/okmsoil.c1-checkpoint.ipynb create mode 100644 VAPs/quicklook/OKMSOIL/OKMSOIL_tutorial.ipynb create mode 100644 VAPs/quicklook/OKMSOIL/okmsoil.c1.ipynb create mode 100644 VAPs/quicklook/OZONE-AIR/OZONE-AIR_tutorial.ipynb create mode 100644 VAPs/quicklook/OZONE-AIR/aafo3.c1.ipynb create mode 100644 VAPs/quicklook/PBLHT/.ipynb_checkpoints/pblhtsonde1mcfarl.c1-checkpoint.ipynb create mode 100644 VAPs/quicklook/PBLHT/PBLHT_tutorial.ipynb create mode 100644 VAPs/quicklook/PBLHT/pblhtsonde1mcfarl.c1.ipynb create mode 100644 VAPs/quicklook/PBLHT/pblhtsondeyr1mcfarl.c1.ipynb create mode 100644 VAPs/quicklook/PCCP/PCCP_tutorial.ipynb create mode 100644 VAPs/quicklook/PCCP/pccp.c1.ipynb create mode 100644 VAPs/quicklook/PSAP/.ipynb_checkpoints/aospsap3w.c1-checkpoint.ipynb create mode 100644 VAPs/quicklook/PSAP/PSAP_tutorial.ipynb create mode 100644 VAPs/quicklook/PSAP/aospsap3w.c1.ipynb create mode 100644 VAPs/quicklook/QCRAD/QCRAD_tutorial.ipynb create mode 100644 VAPs/quicklook/QCRAD/qcrad1long.c1.ipynb create mode 100644 VAPs/quicklook/QCRAD/qcrad1long.c2.ipynb create mode 100644 VAPs/quicklook/QCRAD/qcradbeflux1long.c1.ipynb create mode 100644 VAPs/quicklook/QCRAD/qcradbeflux1long.c2.ipynb create mode 100644 VAPs/quicklook/QCRAD/qcradbrs1long.c1.ipynb create mode 100644 VAPs/quicklook/QCRAD/qcradbrs1long.c2.ipynb create mode 100644 VAPs/quicklook/RADFLUXANAL/.ipynb_checkpoints/radflux1long.c1-checkpoint.ipynb create mode 100644 VAPs/quicklook/RADFLUXANAL/RADFLUXANAL_tutorial.ipynb create mode 100644 VAPs/quicklook/RADFLUXANAL/radflux1long.c1.ipynb create mode 100644 VAPs/quicklook/RADFLUXANAL/radflux1long.c2.ipynb create mode 100644 VAPs/quicklook/RADFLUXANAL/radfluxbrs1long.c2.ipynb create mode 100644 VAPs/quicklook/RIPBE/30ripbe1mcfarlane.c1.ipynb create mode 100644 VAPs/quicklook/RIPBE/RIPBE_tutorial.ipynb create mode 100644 VAPs/quicklook/RIPBE/ripbe1mcfarlane.c1.ipynb create mode 100644 VAPs/quicklook/RLPROF/10rlprofbe1news.c1.ipynb create mode 100644 VAPs/quicklook/RLPROF/RLPROF_tutorial.ipynb create mode 100644 VAPs/quicklook/SACRADV3D3C/.ipynb_checkpoints/kasacradv3d3c.c1-checkpoint.ipynb create mode 100644 VAPs/quicklook/SACRADV3D3C/SACRADV3D3C_tutorial.ipynb create mode 100644 VAPs/quicklook/SACRADV3D3C/kasacradv3d3c.c1.ipynb create mode 100644 VAPs/quicklook/SACRADVVAD/SACRADVVAD_tutorial.ipynb create mode 100644 VAPs/quicklook/SACRADVVAD/kasacradvvad.c1.ipynb create mode 100644 VAPs/quicklook/SFCCLDGRID/.ipynb_checkpoints/15swfcldgrid1long.c1-checkpoint.ipynb create mode 100644 VAPs/quicklook/SFCCLDGRID/.ipynb_checkpoints/sfccldgrid2longcaracena.c1-checkpoint.ipynb create mode 100644 VAPs/quicklook/SFCCLDGRID/15swfcldgrid1long.c1.ipynb create mode 100644 VAPs/quicklook/SFCCLDGRID/SFCCLDGRID_tutorial.ipynb create mode 100644 VAPs/quicklook/SFCCLDGRID/sfccldgrid2longcaracena.c1.ipynb create mode 100644 VAPs/quicklook/SFCCLDGRID/sfccldgrid2longstation.c1.ipynb create mode 100644 VAPs/quicklook/SHALLOWCUMULUS/SHALLOWCUMULUS_tutorial.ipynb create mode 100644 VAPs/quicklook/SHALLOWCUMULUS/shallowcumulus.c1.ipynb create mode 100644 VAPs/quicklook/SHALLOWCUMULUS/shcusummary.c1.ipynb create mode 100644 VAPs/quicklook/SO2-AIR/SO2-AIR_tutorial.ipynb create mode 100644 VAPs/quicklook/SO2-AIR/aafso2.c1.ipynb create mode 100644 VAPs/quicklook/SONDEADJUST/.ipynb_checkpoints/SONDEADJUST_tutorial-checkpoint.ipynb create mode 100644 VAPs/quicklook/SONDEADJUST/SONDEADJUST_tutorial.ipynb create mode 100644 VAPs/quicklook/SONDEADJUST/sondeadjust.c1.ipynb create mode 100644 VAPs/quicklook/SONDEPARAM/SONDEPARAM_tutorial.ipynb create mode 100644 VAPs/quicklook/SONDEPARAM/sondeparam.c1.ipynb create mode 100644 VAPs/quicklook/SP2-AIR/SP2-AIR_tutorial.ipynb create mode 100644 VAPs/quicklook/SP2-AIR/aafsp2rbc10s.c1.ipynb create mode 100644 VAPs/quicklook/SPHOTCOD/SPHOTCOD_tutorial.ipynb create mode 100644 VAPs/quicklook/SPHOTCOD/sphotcod2chiu.c1.ipynb create mode 100644 VAPs/quicklook/SURFSPECALB/SURFSPECALB_tutorial.ipynb create mode 100644 VAPs/quicklook/SURFSPECALB/surfspecalb1mlawer.c1.ipynb create mode 100644 VAPs/quicklook/SURFSPECALB/surfspecalb7nch1mlawer.c1.ipynb create mode 100644 VAPs/quicklook/SWFLUXANAL/.ipynb_checkpoints/1swfanalsirs1long.c1-checkpoint.ipynb create mode 100644 VAPs/quicklook/TBSMERGED/TBSMERGED_tutorial.ipynb create mode 100644 VAPs/quicklook/TBSMERGED/tbsmerged.c1.ipynb create mode 100644 VAPs/quicklook/TBSMERGED/tbsmergedincloud.c1.ipynb create mode 100644 VAPs/quicklook/TDMA/TDMA_tutorial.ipynb create mode 100644 VAPs/quicklook/TDMA/tdmaapssize.c1.ipynb create mode 100644 VAPs/quicklook/TWRMR/1twrmr.c1.ipynb create mode 100644 VAPs/quicklook/TWRMR/30twrmr.c1.ipynb create mode 100644 VAPs/quicklook/TWRMR/TWRMR_tutorial.ipynb create mode 100644 VAPs/quicklook/VARANAL/180varanaecmwf.c1.ipynb create mode 100644 VAPs/quicklook/VARANAL/180varanamerra001.c1.ipynb create mode 100644 VAPs/quicklook/VARANAL/VARANAL_tutorial.ipynb create mode 100644 VAPs/quicklook/VARANAL3D/180varanal3dera5.c1.ipynb create mode 100644 VAPs/quicklook/VARANAL3D/180varanal3dncep.c1.ipynb create mode 100644 VAPs/quicklook/VARANAL3D/VARANAL3D_tutorial.ipynb create mode 100644 VAPs/quicklook/VDISQUANTS/VDISQUANTS_tutorial.ipynb create mode 100644 VAPs/quicklook/VDISQUANTS/vdisquants.c1.ipynb create mode 100644 VAPs/quicklook/WACRARSCL/WACRARSCL_tutorial.ipynb create mode 100644 VAPs/quicklook/WACRARSCL/arsclwacr1kollias.c1.ipynb create mode 100644 VAPs/quicklook/WACRARSCL/arsclwacrbnd1kollias.c1.ipynb create mode 100644 VAPs/vap_notebook_list.md diff --git a/.gitignore b/.gitignore index e43b0f98..c71e30af 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ .DS_Store +_build/ \ No newline at end of file diff --git a/VAPs/quicklook/2DS-AIR/.ipynb_checkpoints/aaf2dsh.c1-checkpoint.ipynb b/VAPs/quicklook/2DS-AIR/.ipynb_checkpoints/aaf2dsh.c1-checkpoint.ipynb new file mode 100644 index 00000000..66d434d8 --- /dev/null +++ b/VAPs/quicklook/2DS-AIR/.ipynb_checkpoints/aaf2dsh.c1-checkpoint.ipynb @@ -0,0 +1,1271 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# AAF2DSH.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/2ds-air) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'aaf2dsh'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2018-12-08', 'facility': 'F1', 'site': 'cor', 'start_date': '2018-11-04'}, {'end_date': '2018-02-19', 'facility': 'F1', 'site': 'ena', 'start_date': '2017-06-21'}, {'end_date': '2016-09-22', 'facility': 'F1', 'site': 'sgp', 'start_date': '2016-04-25'}]" + ] + }, + { + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "ac6764f5", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The following locations and date ranges are available for this VAP:\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
sitefacilitystart_dateend_date
0corF12018-11-042018-12-08
1enaF12017-06-212018-02-19
2sgpF12016-04-252016-09-22
\n", + "
" + ], + "text/plain": [ + " site facility start_date end_date\n", + "0 cor F1 2018-11-04 2018-12-08\n", + "1 ena F1 2017-06-21 2018-02-19\n", + "2 sgp F1 2016-04-25 2016-09-22" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'F1' )\n", + "\n", + "date_start = '2016-09-21'\n", + "date_end = '2016-09-22'" + ] + }, + { + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'/data/archive/sgp/sgpaaf2dshF1.c1'" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['20160921', '20160922']" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "51feea2e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['/data/archive/sgp/sgpaaf2dshF1.c1/sgpaaf2dshF1.c1.20160921.163940.nc',\n", + " '/data/archive/sgp/sgpaaf2dshF1.c1/sgpaaf2dshF1.c1.20160922.160625.nc']" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2 files loaded\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
<xarray.Dataset>\n",
+       "Dimensions:                     (time: 18380, optical_diameter: 61, bound: 2)\n",
+       "Coordinates:\n",
+       "  * time                        (time) datetime64[ns] 2016-09-21T16:39:40 ......\n",
+       "  * optical_diameter            (optical_diameter) float32 10.0 20.0 ... inf\n",
+       "Dimensions without coordinates: bound\n",
+       "Data variables:\n",
+       "    base_time                   (time) datetime64[ns] 2016-09-21 ... 2016-09-22\n",
+       "    time_offset                 (time) datetime64[ns] 2016-09-21T16:39:40 ......\n",
+       "    optical_diameter_bounds     (time, optical_diameter, bound) float32 dask.array<chunksize=(8283, 61, 2), meta=np.ndarray>\n",
+       "    total_number_concentration  (time) float32 dask.array<chunksize=(8283,), meta=np.ndarray>\n",
+       "    number_concentration        (time, optical_diameter) float32 dask.array<chunksize=(8283, 61), meta=np.ndarray>\n",
+       "    lat                         (time) float32 dask.array<chunksize=(8283,), meta=np.ndarray>\n",
+       "    lon                         (time) float32 dask.array<chunksize=(8283,), meta=np.ndarray>\n",
+       "    alt                         (time) float32 dask.array<chunksize=(8283,), meta=np.ndarray>\n",
+       "Attributes: (12/17)\n",
+       "    command_line:          aaf2dsme_ingest -s sgp -f F1 -D -R\n",
+       "    Conventions:           ARM-1.3\n",
+       "    process_version:       ingest-aaf2dsme-1.2-0.el7\n",
+       "    dod_version:           aaf2dsh-c1-1.1\n",
+       "    input_source:          /data/project/ENG0004504/collection/sgp/sgpaaf2dsF...\n",
+       "    site_id:               sgp\n",
+       "    ...                    ...\n",
+       "    doi:                   10.5439/1419322\n",
+       "    history:               created by user burk on machine prod-proc5.adc.arm...\n",
+       "    _file_dates:           ['20160921', '20160922']\n",
+       "    _file_times:           ['163940', '160625']\n",
+       "    _datastream:           sgpaaf2dshF1.c1\n",
+       "    _arm_standards_flag:   1
" + ], + "text/plain": [ + "\n", + "Dimensions: (time: 18380, optical_diameter: 61, bound: 2)\n", + "Coordinates:\n", + " * time (time) datetime64[ns] 2016-09-21T16:39:40 ......\n", + " * optical_diameter (optical_diameter) float32 10.0 20.0 ... inf\n", + "Dimensions without coordinates: bound\n", + "Data variables:\n", + " base_time (time) datetime64[ns] 2016-09-21 ... 2016-09-22\n", + " time_offset (time) datetime64[ns] 2016-09-21T16:39:40 ......\n", + " optical_diameter_bounds (time, optical_diameter, bound) float32 dask.array\n", + " total_number_concentration (time) float32 dask.array\n", + " number_concentration (time, optical_diameter) float32 dask.array\n", + " lat (time) float32 dask.array\n", + " lon (time) float32 dask.array\n", + " alt (time) float32 dask.array\n", + "Attributes: (12/17)\n", + " command_line: aaf2dsme_ingest -s sgp -f F1 -D -R\n", + " Conventions: ARM-1.3\n", + " process_version: ingest-aaf2dsme-1.2-0.el7\n", + " dod_version: aaf2dsh-c1-1.1\n", + " input_source: /data/project/ENG0004504/collection/sgp/sgpaaf2dsF...\n", + " site_id: sgp\n", + " ... ...\n", + " doi: 10.5439/1419322\n", + " history: created by user burk on machine prod-proc5.adc.arm...\n", + " _file_dates: ['20160921', '20160922']\n", + " _file_times: ['163940', '160625']\n", + " _datastream: sgpaaf2dshF1.c1\n", + " _arm_standards_flag: 1" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['total_number_concentration', 'number_concentration']" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [ + { + "ename": "ValueError", + "evalue": "x and y arguments to pcolormesh cannot have non-finite values or be of type numpy.ma.core.MaskedArray with masked values", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[0;32mIn[9], line 5\u001b[0m\n\u001b[1;32m 2\u001b[0m ts_display\u001b[38;5;241m.\u001b[39madd_subplots((\u001b[38;5;28mlen\u001b[39m(variables_to_plot),), figsize \u001b[38;5;241m=\u001b[39m (\u001b[38;5;241m9.5\u001b[39m,\u001b[38;5;241m4\u001b[39m\u001b[38;5;241m*\u001b[39m\u001b[38;5;28mlen\u001b[39m(variables_to_plot)))\n\u001b[1;32m 4\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m i,v \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28menumerate\u001b[39m(variables_to_plot):\n\u001b[0;32m----> 5\u001b[0m ts_ax \u001b[38;5;241m=\u001b[39m \u001b[43mts_display\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mplot\u001b[49m\u001b[43m(\u001b[49m\u001b[43mv\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43msubplot_index\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mi\u001b[49m\u001b[43m,\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mset_title\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mds\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mvariables\u001b[49m\u001b[43m[\u001b[49m\u001b[43mv\u001b[49m\u001b[43m]\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mattrs\u001b[49m\u001b[43m[\u001b[49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43mlong_name\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m,\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 6\u001b[0m ts_ax\u001b[38;5;241m.\u001b[39mgrid()\n\u001b[1;32m 8\u001b[0m plt\u001b[38;5;241m.\u001b[39mshow()\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/act/plotting/timeseriesdisplay.py:588\u001b[0m, in \u001b[0;36mTimeSeriesDisplay.plot\u001b[0;34m(self, field, dsname, subplot_index, cmap, set_title, add_nan, day_night_background, invert_y_axis, abs_limits, time_rng, y_rng, use_var_for_y, set_shading, assessment_overplot, overplot_marker, overplot_behind, overplot_markersize, assessment_overplot_category, assessment_overplot_category_color, force_line_plot, labels, cbar_label, cbar_h_adjust, secondary_y, y_axis_flag_meanings, colorbar_labels, cb_friendly, **kwargs)\u001b[0m\n\u001b[1;32m 586\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124medgecolors\u001b[39m\u001b[38;5;124m'\u001b[39m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;129;01min\u001b[39;00m kwargs\u001b[38;5;241m.\u001b[39mkeys():\n\u001b[1;32m 587\u001b[0m kwargs[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124medgecolors\u001b[39m\u001b[38;5;124m'\u001b[39m] \u001b[38;5;241m=\u001b[39m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mface\u001b[39m\u001b[38;5;124m'\u001b[39m\n\u001b[0;32m--> 588\u001b[0m mesh \u001b[38;5;241m=\u001b[39m \u001b[43max\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mpcolormesh\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 589\u001b[0m \u001b[43m \u001b[49m\u001b[43mnp\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43masarray\u001b[49m\u001b[43m(\u001b[49m\u001b[43mxdata\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 590\u001b[0m \u001b[43m \u001b[49m\u001b[43mydata\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 591\u001b[0m \u001b[43m \u001b[49m\u001b[43mdata\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mtranspose\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 592\u001b[0m \u001b[43m \u001b[49m\u001b[43mshading\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mset_shading\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 593\u001b[0m \u001b[43m \u001b[49m\u001b[43mcmap\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcmap\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 594\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 595\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 597\u001b[0m \u001b[38;5;66;03m# Set Title\u001b[39;00m\n\u001b[1;32m 598\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m set_title \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/matplotlib/__init__.py:1442\u001b[0m, in \u001b[0;36m_preprocess_data..inner\u001b[0;34m(ax, data, *args, **kwargs)\u001b[0m\n\u001b[1;32m 1439\u001b[0m \u001b[38;5;129m@functools\u001b[39m\u001b[38;5;241m.\u001b[39mwraps(func)\n\u001b[1;32m 1440\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21minner\u001b[39m(ax, \u001b[38;5;241m*\u001b[39margs, data\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mNone\u001b[39;00m, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs):\n\u001b[1;32m 1441\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m data \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[0;32m-> 1442\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mfunc\u001b[49m\u001b[43m(\u001b[49m\u001b[43max\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;28;43mmap\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43msanitize_sequence\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43margs\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 1444\u001b[0m bound \u001b[38;5;241m=\u001b[39m new_sig\u001b[38;5;241m.\u001b[39mbind(ax, \u001b[38;5;241m*\u001b[39margs, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs)\n\u001b[1;32m 1445\u001b[0m auto_label \u001b[38;5;241m=\u001b[39m (bound\u001b[38;5;241m.\u001b[39marguments\u001b[38;5;241m.\u001b[39mget(label_namer)\n\u001b[1;32m 1446\u001b[0m \u001b[38;5;129;01mor\u001b[39;00m bound\u001b[38;5;241m.\u001b[39mkwargs\u001b[38;5;241m.\u001b[39mget(label_namer))\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/matplotlib/axes/_axes.py:6220\u001b[0m, in \u001b[0;36mAxes.pcolormesh\u001b[0;34m(self, alpha, norm, cmap, vmin, vmax, shading, antialiased, *args, **kwargs)\u001b[0m\n\u001b[1;32m 6217\u001b[0m shading \u001b[38;5;241m=\u001b[39m shading\u001b[38;5;241m.\u001b[39mlower()\n\u001b[1;32m 6218\u001b[0m kwargs\u001b[38;5;241m.\u001b[39msetdefault(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124medgecolors\u001b[39m\u001b[38;5;124m'\u001b[39m, \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mnone\u001b[39m\u001b[38;5;124m'\u001b[39m)\n\u001b[0;32m-> 6220\u001b[0m X, Y, C, shading \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_pcolorargs\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43mpcolormesh\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 6221\u001b[0m \u001b[43m \u001b[49m\u001b[43mshading\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mshading\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mkwargs\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 6222\u001b[0m coords \u001b[38;5;241m=\u001b[39m np\u001b[38;5;241m.\u001b[39mstack([X, Y], axis\u001b[38;5;241m=\u001b[39m\u001b[38;5;241m-\u001b[39m\u001b[38;5;241m1\u001b[39m)\n\u001b[1;32m 6223\u001b[0m \u001b[38;5;66;03m# convert to one dimensional array, except for 3D RGB(A) arrays\u001b[39;00m\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/matplotlib/axes/_axes.py:5717\u001b[0m, in \u001b[0;36mAxes._pcolorargs\u001b[0;34m(self, funcname, shading, *args, **kwargs)\u001b[0m\n\u001b[1;32m 5715\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m funcname \u001b[38;5;241m==\u001b[39m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mpcolormesh\u001b[39m\u001b[38;5;124m'\u001b[39m:\n\u001b[1;32m 5716\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m np\u001b[38;5;241m.\u001b[39mma\u001b[38;5;241m.\u001b[39mis_masked(X) \u001b[38;5;129;01mor\u001b[39;00m np\u001b[38;5;241m.\u001b[39mma\u001b[38;5;241m.\u001b[39mis_masked(Y):\n\u001b[0;32m-> 5717\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\n\u001b[1;32m 5718\u001b[0m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mx and y arguments to pcolormesh cannot have \u001b[39m\u001b[38;5;124m'\u001b[39m\n\u001b[1;32m 5719\u001b[0m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mnon-finite values or be of type \u001b[39m\u001b[38;5;124m'\u001b[39m\n\u001b[1;32m 5720\u001b[0m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mnumpy.ma.core.MaskedArray with masked values\u001b[39m\u001b[38;5;124m'\u001b[39m)\n\u001b[1;32m 5721\u001b[0m \u001b[38;5;66;03m# safe_masked_invalid() returns an ndarray for dtypes other\u001b[39;00m\n\u001b[1;32m 5722\u001b[0m \u001b[38;5;66;03m# than floating point.\u001b[39;00m\n\u001b[1;32m 5723\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(X, np\u001b[38;5;241m.\u001b[39mma\u001b[38;5;241m.\u001b[39mcore\u001b[38;5;241m.\u001b[39mMaskedArray):\n", + "\u001b[0;31mValueError\u001b[0m: x and y arguments to pcolormesh cannot have non-finite values or be of type numpy.ma.core.MaskedArray with masked values" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "eae83e9e4798482083ecdb12aeb73cf7", + "version_major": 2, + "version_minor": 0 + }, + "image/png": "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ufvs53B4F8G2hjp27JgkKTEx0ccjAQAAAC4fx44dk91u9/UwLjs2w58UaqSysjLt27dPERERstlsvh6OpDN/xUpMTNSePXsUGRnp6+FYHvV0P2rqXtTTvaine1FP96Ke7kU93ctb9TTG6NixY0pISFCtWnzj09s4YltD1apVS/Xr1/f1MCoUGRnJL2k3op7uR03di3q6F/V0L+rpXtTTvaine3mjnhyp9R3+lAAAAAAAsDSCLQAAAADA0gi28JqQkBCNGzdOISEhvh5KjUA93Y+auhf1dC/q6V7U072op3tRT/einpcHLh4FAAAAALA0jtgCAAAAACyNYAsAAAAAsDSCLQAAAADA0gi2AAAAAABLI9iiWj7//HPde++9SkhIkM1m0wcffODy/PHjx/Xkk0+qfv36CgsLU9OmTTVz5swqt7t582a1a9dOYWFhqlevniZMmKBzr2e2cuVKpaSkKDQ0VFdffbVef/11d07N6zIyMnTrrbcqIiJCMTExuv/++7Vjxw6XPu+//746d+6s6Oho2Ww2ZWdnV2vbl2M9z5oxY4aSkpIUGhqqlJQUrVq1yvmcMUbp6elKSEhQWFiY2rdvr61bt1a5zcu5ntL5a5qenq7rr79etWvXVp06ddSxY0etX7++ym1ezjU9Xz0lafv27erRo4fsdrsiIiLUunVr5eTknHeb1LPieu7fv18DBgxQQkKCwsPD1aVLF3333XdVbvNyref5/o0vKSnRs88+q2bNmql27dpKSEhQv379tG/fviq3Sz0r/sw0YMAA2Ww2l0fr1q2r3C715DMoqmCAali6dKkZO3asee+994wks3jxYpfnH330UXPNNdeYFStWmJ07d5pZs2aZgIAA88EHH1S6TYfDYWJjY82DDz5oNm/ebN577z0TERFh/vjHPzr7/PjjjyY8PNw8/fTTZtu2bWb27NkmKCjI/P3vf/fUVD2uc+fOZt68eWbLli0mOzvb3HPPPaZBgwbm+PHjzj5vv/22GT9+vJk9e7aRZLKysqrc7uVaT2OMWbhwoQkKCjKzZ88227ZtM08//bSpXbu22b17tzHGmJdeeslERESY9957z2zevNn07t3bxMfHm4KCgkq3eTnX05iqa7pgwQKTmZlpfvjhB7NlyxYzcOBAExkZafLz8yvd5uVc06rq+f3335uoqCjz29/+1nz55Zfmhx9+MB9++KHZv39/pduknhXXs6yszLRu3drccccdZsOGDeabb74xjz32WLnfs+e6nOt5vn/jjx49ajp27GgWLVpkvvnmG7N27VrTqlUrk5KSct5tUs/KPzP179/fdOnSxeTm5jofhw4dOu82qSefQVE1gi0uWEW/VG688UYzYcIEl7ZbbrnF/O53v6t0OzNmzDB2u92cOnXK2ZaRkWESEhJMWVmZMcaYZ555xlx//fUu6z3++OOmdevWlzgL/5Gfn28kmZUrV5Z7bufOndUOtpdzPW+77TYzePBgl7brr7/ejB492pSVlZm4uDjz0ksvOZ87deqUsdvt5vXXX690m5dzPY05f00r4nA4jCTzySefVLrNy7mmVdWzd+/e5te//vUFbZN6VlzPHTt2GElmy5YtzudOnz5toqKizOzZsyvd5uVcz1+q6N/4c23YsMFIcv5hpiLU84zKgu199913QduhnmfwGRTnw6nIcIvbb79dS5Ys0U8//SRjjFasWKFvv/1WnTt3dvYZMGCA2rdv71xeu3at2rVr53Kz7M6dO2vfvn3atWuXs09aWprLvjp37qwvvvhCJSUlHp2TtzgcDklSVFTUBa1HPc8oLi7Wpk2bys0rLS1Na9as0c6dO5WXl+fyfEhIiNq1a6c1a9Y426jnz6qqaUX933jjDdntdrVo0cLZTk3PqKqeZWVl+uijj3Tdddepc+fOiomJUatWrSo8fZF6Vl3PoqIiSVJoaKjzuYCAAAUHB2v16tXONup58RwOh2w2m6688kpnG/W8MJ999pliYmJ03XXXadCgQcrPz3d5nnpWH59BcRbBFm7x5z//WTfccIPq16+v4OBgdenSRTNmzNDtt9/u7BMfH68GDRo4l/Py8hQbG+uynbPLeXl55+1z+vRpHTx40FPT8RpjjEaMGKHbb79dycnJF7Qu9Tzj4MGDKi0trXBeeXl5zrlX9vxZ1PNnVdX0rA8//FBXXHGFQkND9eqrryozM1PR0dHO56npGVXVMz8/X8ePH9dLL72kLl26aPny5XrggQfUs2dPrVy50tmfep5RVT2vv/56NWzYUGPGjNGRI0dUXFysl156SXl5ecrNzXX2p54X59SpUxo9erT69OmjyMhIZzv1rL6uXbtqwYIF+vTTT/XKK69o48aNuuuuu5x/lJGo54XgMyjOCvT1AFAz/PnPf9a6deu0ZMkSNWzYUJ9//rmGDBmi+Ph4dezYUdKZiyady2azuSyb/35p/5ft1eljVU8++aS+/vprl6MI1UU9XVU0r6rm/cs26lleVTXr0KGDsrOzdfDgQc2ePVu9evXS+vXrFRMTI4manquyepaVlUmS7rvvPg0fPlySdNNNN2nNmjV6/fXX1a5dO0nU81yV1TMoKEjvvfeeBg4cqKioKAUEBKhjx47q2rWrS3/qeeFKSkr04IMPqqysTDNmzHB5jnpWX+/evZ3/n5ycrJYtW6phw4b66KOP1LNnT0nU80LwGRRnEWxxyQoLC/Xcc89p8eLFuueeeyRJzZs3V3Z2tv74xz86f6mcKy4uzuXojyTnqThn/0JWWZ/AwEDVrVvX3VPxqqeeekpLlizR559/rvr161/y9i7XekZHRysgIKDCecXGxiouLk7Smb+8xsfHl3u+MpdrPaWqa3pW7dq1de211+raa69V69at1bhxY82ZM0djxoypcLuXa02rqmd0dLQCAwN1ww03uDzftGnT8/7Ri3pW/v5MSUlRdna2HA6HiouLddVVV6lVq1Zq2bJlpdu9XOtZXSUlJerVq5d27typTz/91OVobUWoZ/XFx8erYcOG571yN/WsGJ9B8UucioxLVlJSopKSEtWq5fp2CggIcB6JqEhqaqo+//xzFRcXO9uWL1+uhIQENWrUyNknMzPTZb3ly5erZcuWCgoKct8kvMgYoyeffFLvv/++Pv30UyUlJbllu5drPYODg5WSklJuXpmZmWrTpo2SkpIUFxfn8nxxcbFWrlypNm3aVLrdy7WeUtU1rYwxxuVUunNdrjWtqp7BwcG69dZby93269tvv1XDhg0r3S71rPr9abfbddVVV+m7777TF198ofvuu6/S7V6u9ayOs6H2u+++0yeffFKtD/XUs/oOHTqkPXv2uPzx9VzUs2J8BoUL71yjClZ37Ngxk5WVZbKysowkM2XKFJOVleW8ImK7du3MjTfeaFasWGF+/PFHM2/ePBMaGmpmzJjh3Mbo0aNN3759nctHjx41sbGx5qGHHjKbN28277//vomMjKzwUuvDhw8327ZtM3PmzLH8pdb/93//19jtdvPZZ5+5XOr/5MmTzj6HDh0yWVlZ5qOPPjKSzMKFC01WVpbJzc119qGePzt76485c+aYbdu2mWHDhpnatWubXbt2GWPO3O7H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oOrWrVvhOiEhIQoJCSnXbrfb+QVUDZGRkdSpCtSoeqhT1ahR9VCn6qFOVaNG1UOdqkaNqqdWLa7P6wtU3cOCg4OVkpKizMxMl/bMzEy1adOmwnVSU1PL9V++fLlatmxZ4fdrAQAAAOByRrD1ghEjRugvf/mL5s6dq+3bt2v48OHKycnR4MGDJZ05jbhfv37O/oMHD9bu3bs1YsQIbd++XXPnztWcOXM0atQoX00BAAAAAPwWpyJ7Qe/evXXo0CFNmDBBubm5Sk5O1tKlS9WwYUNJUm5urss9bZOSkrR06VINHz5cr732mhISEvTnP/9Zv/rVr6q9z5CQEI0bN67C05PxM+pUNWpUPdSpatSoeqhT9VCnqlGj6qFOVaNG1UOdfIv72AIAAAAALI1TkQEAAAAAlkawBQAAAABYGsEWAAAAAGBpBFsAAAAAgKURbP1URkaGbr31VkVERCgmJkb333+/duzY4dLHGKP09HQlJCQoLCxM7du319atW136FBUV6amnnlJ0dLRq166tHj16aO/evS59jhw5or59+8put8tut6tv3746evSop6foFt6s04svvqg2bdooPDxcV155paen5jbeqtGuXbs0cOBAJSUlKSwsTNdcc43GjRun4uJir8zzUnnzvdSjRw81aNBAoaGhio+PV9++fbVv3z6Pz/FSebNGv+x70003yWazKTs721NTcytv1qlRo0ay2Wwuj9GjR3t8ju7g7ffTRx99pFatWiksLEzR0dHq2bOnR+fnDt6q0WeffVbufXT2sXHjRq/M9VJ487307bff6r777lN0dLQiIyPVtm1brVixwuNzdAdv1unLL79Up06ddOWVV6pu3bp67LHHdPz4cY/P8VK5q0ZvvPGG2rdvr8jISNlstgo/V1v587ffMvBLnTt3NvPmzTNbtmwx2dnZ5p577jENGjQwx48fd/Z56aWXTEREhHnvvffM5s2bTe/evU18fLwpKChw9hk8eLCpV6+eyczMNF9++aXp0KGDadGihTl9+rSzT5cuXUxycrJZs2aNWbNmjUlOTjbdu3f36nwvljfr9MILL5gpU6aYESNGGLvd7s1pXhJv1ehf//qXGTBggPn444/NDz/8YP7xj3+YmJgYM3LkSK/P+WJ48700ZcoUs3btWrNr1y7zn//8x6SmpprU1FSvzvdieLNGZw0dOtR07drVSDJZWVnemOYl82adGjZsaCZMmGByc3Odj2PHjnl1vhfLm3X6+9//burUqWNmzpxpduzYYb755hvzt7/9zavzvRjeqlFRUZHLeyg3N9c8+uijplGjRqasrMzr875Q3nwvXXvttaZbt27mq6++Mt9++60ZMmSICQ8PN7m5uV6d88XwVp1++uknU6dOHTN48GDzzTffmA0bNpg2bdqYX/3qV16f84VyV41effVVk5GRYTIyMowkc+TIkXL7svLnb39FsLWI/Px8I8msXLnSGGNMWVmZiYuLMy+99JKzz6lTp4zdbjevv/66McaYo0ePmqCgILNw4UJnn59++snUqlXLLFu2zBhjzLZt24wks27dOmeftWvXGknmm2++8cbU3MpTdfqlefPmWSrYnssbNTpr8uTJJikpyUMz8Sxv1ukf//iHsdlspri42EOz8QxP12jp0qXm+uuvN1u3brVUsD2XJ+vUsGFD8+qrr3pnIh7mqTqVlJSYevXqmb/85S9enI1neOv3UnFxsYmJiTETJkzw4Gw8x1N1OnDggJFkPv/8c2efgoICI8l88skn3piaW3mqTrNmzTIxMTGmtLTU2ScrK8tIMt999503puY2F1OjX1qxYkWFwbamff72F5yKbBEOh0OSFBUVJUnauXOn8vLylJaW5uwTEhKidu3aac2aNZKkTZs2qaSkxKVPQkKCkpOTnX3Wrl0ru92uVq1aOfu0bt1adrvd2cdKPFWnmsSbNXI4HM79WI236nT48GEtWLBAbdq0UVBQkKem4xGerNH+/fs1aNAg/fWvf1V4eLg3puMxnn4vTZo0SXXr1tVNN92kF1980TKn/5/LU3X68ssv9dNPP6lWrVq6+eabFR8fr65du5Y7ddAKvPV7acmSJTp48KAGDBjgoZl4lqfqVLduXTVt2lRvv/22Tpw4odOnT2vWrFmKjY1VSkqKt6bnNp6qU1FRkYKDg1Wr1s8xIywsTJK0evVqz07KzS6mRtVR0z5/+wuCrQUYYzRixAjdfvvtSk5OliTl5eVJkmJjY136xsbGOp/Ly8tTcHCw6tSpc94+MTEx5fYZExPj7GMVnqxTTeHNGv3www+aNm2aBg8e7O5peJw36vTss8+qdu3aqlu3rnJycvSPf/zDU9PxCE/WyBijAQMGaPDgwWrZsqWnp+JRnn4vPf3001q4cKFWrFihJ598UlOnTtWQIUM8OSWP8GSdfvzxR0lSenq6fve73+nDDz9UnTp11K5dOx0+fNij83Inb/7+njNnjjp37qzExER3T8PjPFknm82mzMxMZWVlKSIiQqGhoXr11Ve1bNkyS117Q/Jsne666y7l5eXp5ZdfVnFxsY4cOaLnnntOkpSbm+vRebnTxdaoOmrS529/QrC1gCeffFJff/213n333XLP2Ww2l2VjTLm2c53bp6L+1dmOv/F0nWoCb9Vo37596tKli/7f//t/evTRRy9t0D7gjTr99re/VVZWlpYvX66AgAD169dPxphLH7yXeLJG06ZNU0FBgcaMGeO+AfuIp99Lw4cPV7t27dS8eXM9+uijev311zVnzhwdOnTIPRPwEk/WqaysTJI0duxY/epXv1JKSormzZsnm82mv/3tb26aged56/f33r179fHHH2vgwIGXNmAf8WSdjDEaMmSIYmJitGrVKm3YsEH33XefunfvbqnAJnm2TjfeeKPeeustvfLKKwoPD1dcXJyuvvpqxcbGKiAgwH2T8DB316iqbVzsdvAzgq2fe+qpp7RkyRKtWLFC9evXd7bHxcVJUrm/6uTn5zv/ihQXF+f8S9n5+uzfv7/cfg8cOFDur1H+zNN1qgm8VaN9+/apQ4cOSk1N1RtvvOGJqXiUt+oUHR2t6667Tp06ddLChQu1dOlSrVu3zhNTcjtP1+jTTz/VunXrFBISosDAQF177bWSpJYtW6p///4em5e7+eL3UuvWrSVJ33//vVvm4A2erlN8fLwk6YYbbnA+HxISoquvvlo5OTnun5AHePO9NG/ePNWtW1c9evRw9zQ8zhu/mz788EMtXLhQbdu21S233KIZM2YoLCxMb731lien5lbeeD/16dNHeXl5+umnn3To0CGlp6frwIEDSkpK8tS03OpSalQdNeXzt9/x6Dd4cdHKysrME088YRISEsy3335b4fNxcXFm0qRJzraioqIKv+C/aNEiZ599+/ZVePGo9evXO/usW7fOMl9e91adfslqF4/yZo327t1rGjdubB588MEKr3Drz3zxXjorJyfHSDIrVqxw34Q8wFs12r17t9m8ebPz8fHHHxtJ5u9//7vZs2ePh2d56Xz5XvrnP/9pJJndu3e7cUae4a06ORwOExIS4nLxqLMXR5o1a5anpucW3n4vlZWVmaSkJMtczf4sb9VpyZIlplatWuWuPH7dddeZF1980RNTcytf/m6aM2eOCQ8Pr/DqwP7EHTX6paouHmXVz9/+imDrp/73f//X2O1289lnn7lcfv/kyZPOPi+99JKx2+3m/fffN5s3bzYPPfRQhZdkr1+/vvnkk0/Ml19+ae66664Kb/fTvHlzs3btWrN27VrTrFkzy1xu3Jt12r17t8nKyjLjx483V1xxhcnKyjJZWVl+f2sNb9Xop59+Mtdee6256667zN69e132ZQXeqtP69evNtGnTTFZWltm1a5f59NNPze23326uueYac+rUKa/P+0J48+ftl3bu3GmpqyJ7q05r1qwxU6ZMMVlZWebHH380ixYtMgkJCaZHjx5en/PF8Ob76emnnzb16tUzH3/8sfnmm2/MwIEDTUxMjDl8+LBX53yhvP0z98knnxhJZtu2bV6bozt4q04HDhwwdevWNT179jTZ2dlmx44dZtSoUSYoKMhkZ2d7fd4Xypvvp2nTpplNmzaZHTt2mOnTp5uwsDDzpz/9yavzvRjuqlFubq7Jysoys2fPdl5JOysryxw6dMjZx8qfv/0VwdZPSarwMW/ePGefsrIyM27cOBMXF2dCQkLMnXfeaTZv3uyyncLCQvPkk0+aqKgoExYWZrp3725ycnJc+hw6dMg8/PDDJiIiwkRERJiHH37Y7/+idpY369S/f/8K9+XvR9m8VaN58+ZVui8r8Fadvv76a9OhQwcTFRVlQkJCTKNGjczgwYPN3r17vTXVi+bNn7dfslqw9VadNm3aZFq1amXsdrsJDQ01TZo0MePGjTMnTpzw1lQviTffT8XFxWbkyJEmJibGREREmI4dO5otW7Z4Y5qXxNs/cw899JBp06aNp6fldt6s08aNG01aWpqJiooyERERpnXr1mbp0qXemOYl82ad+vbta6KiokxwcLBp3ry5efvtt70xxUvmrhqNGzeuyu1Y+fO3v7IZY6GrlQAAAAAAcA4uHgUAAAAAsDSCLQAAAADA0gi2AAAAAABLI9gCAAAAACyNYAsAAAAAsDSCLQAAAADA0gi2AAAAAABLI9gCAAAAACyNYAsAAAAAsDSCLQAAAADA0gi2AAAAAABLI9gCAAAAACyNYAsAAAAAsDSCLQAAAADA0gi2AAAAAABLI9gCAAAAACyNYAsAAAAAsDSCLQAAAADA0gi2AAAAAABLI9gCAAAAACyNYAsAAAAAsDSCLQAAAADA0gi2AAAAAABLI9gCAAAAACyNYAsAAAAAsDSCLQAAAADA0gi2AAAAAABLI9gCAAAAACyNYAsAAAAAsDSCLQAAAADA0gi2AAAAAABLI9gCAAAAACyNYAsAAAAAsDSCLQAAAADA0gi2AAAAAABLI9gCAAAAACyNYAsAAAAAsDSCLQAAAADA0gi2AAAAAABLI9gCAAAAACyNYAsAAAAAsDSCLQAAAADA0gi2AAAAAABLI9gCAAAAACyNYAsAAAAAsDSCLQAAAADA0gi2AAAAAABLI9gCAAAAACyNYAsAAAAAsDSCLQAAAADA0gi2AAAAAABLI9gCAAAAACyNYAsAAAAAsDSCLQAAAADA0gi2AAAAAABLI9gCAAAAACyNYAsAAAAAsDSCLQAAAADA0gi2AAAAAABLI9gCAAAAACyNYAsAAAAAsDSCLQAAAADA0gi2AAAAAABLI9gCAAAAACyNYAsAAAAAsDSCLQAAAADA0v4/FN3X2Uk25/AAAAAASUVORK5CYII=", + "text/html": [ + "\n", + "
\n", + "
\n", + " Figure\n", + "
\n", + " \n", + "
\n", + " " + ], + "text/plain": [ + "Canvas(toolbar=Toolbar(toolitems=[('Home', 'Reset original view', 'home', 'home'), ('Back', 'Back to previous …" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'total_number_concentration'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/2DS-AIR/.ipynb_checkpoints/aaf2dsv.c1-checkpoint.ipynb b/VAPs/quicklook/2DS-AIR/.ipynb_checkpoints/aaf2dsv.c1-checkpoint.ipynb new file mode 100644 index 00000000..9a856041 --- /dev/null +++ b/VAPs/quicklook/2DS-AIR/.ipynb_checkpoints/aaf2dsv.c1-checkpoint.ipynb @@ -0,0 +1,1798 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# AAF2DSV.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/2ds-air) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'aaf2dsv'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2018-12-08', 'facility': 'F1', 'site': 'cor', 'start_date': '2018-11-04'}, {'end_date': '2018-02-19', 'facility': 'F1', 'site': 'ena', 'start_date': '2017-06-21'}, {'end_date': '2016-09-22', 'facility': 'F1', 'site': 'sgp', 'start_date': '2016-04-25'}]" + ] + }, + { + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "ac6764f5", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The following locations and date ranges are available for this VAP:\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
sitefacilitystart_dateend_date
0corF12018-11-042018-12-08
1enaF12017-06-212018-02-19
2sgpF12016-04-252016-09-22
\n", + "
" + ], + "text/plain": [ + " site facility start_date end_date\n", + "0 cor F1 2018-11-04 2018-12-08\n", + "1 ena F1 2017-06-21 2018-02-19\n", + "2 sgp F1 2016-04-25 2016-09-22" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'F1' )\n", + "\n", + "date_start = '2016-09-21'\n", + "date_end = '2016-09-22'" + ] + }, + { + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'/data/archive/sgp/sgpaaf2dsvF1.c1'" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['20160921', '20160922']" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "51feea2e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['/data/archive/sgp/sgpaaf2dsvF1.c1/sgpaaf2dsvF1.c1.20160921.163940.nc',\n", + " '/data/archive/sgp/sgpaaf2dsvF1.c1/sgpaaf2dsvF1.c1.20160922.160625.nc']" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "226f29ae", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
<xarray.Dataset>\n",
+       "Dimensions:                     (time: 10097, optical_diameter: 61, bound: 2)\n",
+       "Coordinates:\n",
+       "  * time                        (time) datetime64[ns] 2016-09-22T16:06:25 ......\n",
+       "  * optical_diameter            (optical_diameter) float32 10.0 20.0 ... inf\n",
+       "Dimensions without coordinates: bound\n",
+       "Data variables:\n",
+       "    base_time                   datetime64[ns] 2016-09-22\n",
+       "    time_offset                 (time) datetime64[ns] 2016-09-22T16:06:25 ......\n",
+       "    optical_diameter_bounds     (optical_diameter, bound) float32 5.0 ... inf\n",
+       "    total_number_concentration  (time) float32 26.59 0.0 22.4 ... 0.0 0.0 nan\n",
+       "    number_concentration        (time, optical_diameter) float32 2.24 ... nan\n",
+       "    lat                         (time) float32 36.76 36.76 36.76 ... 36.74 36.74\n",
+       "    lon                         (time) float32 -96.01 -96.01 ... -96.02 -96.02\n",
+       "    alt                         (time) float32 220.0 224.0 228.0 ... 677.0 674.0\n",
+       "Attributes: (12/13)\n",
+       "    command_line:          aaf2dsme_ingest -s sgp -f F1 -D -R\n",
+       "    Conventions:           ARM-1.3\n",
+       "    process_version:       ingest-aaf2dsme-1.2-0.el7\n",
+       "    dod_version:           aaf2dsv-c1-1.1\n",
+       "    input_source:          /data/project/ENG0004504/collection/sgp/sgpaaf2dsF...\n",
+       "    site_id:               sgp\n",
+       "    ...                    ...\n",
+       "    facility_id:           F1\n",
+       "    data_level:            c1\n",
+       "    location_description:  Southern Great Plains (SGP), Gulfstream 159 ("G1")...\n",
+       "    datastream:            sgpaaf2dsvF1.c1\n",
+       "    doi:                   10.5439/1419323\n",
+       "    history:               created by user burk on machine prod-proc5.adc.arm...
" + ], + "text/plain": [ + "\n", + "Dimensions: (time: 10097, optical_diameter: 61, bound: 2)\n", + "Coordinates:\n", + " * time (time) datetime64[ns] 2016-09-22T16:06:25 ......\n", + " * optical_diameter (optical_diameter) float32 10.0 20.0 ... inf\n", + "Dimensions without coordinates: bound\n", + "Data variables:\n", + " base_time datetime64[ns] 2016-09-22\n", + " time_offset (time) datetime64[ns] 2016-09-22T16:06:25 ......\n", + " optical_diameter_bounds (optical_diameter, bound) float32 5.0 ... inf\n", + " total_number_concentration (time) float32 26.59 0.0 22.4 ... 0.0 0.0 nan\n", + " number_concentration (time, optical_diameter) float32 2.24 ... nan\n", + " lat (time) float32 36.76 36.76 36.76 ... 36.74 36.74\n", + " lon (time) float32 -96.01 -96.01 ... -96.02 -96.02\n", + " alt (time) float32 220.0 224.0 228.0 ... 677.0 674.0\n", + "Attributes: (12/13)\n", + " command_line: aaf2dsme_ingest -s sgp -f F1 -D -R\n", + " Conventions: ARM-1.3\n", + " process_version: ingest-aaf2dsme-1.2-0.el7\n", + " dod_version: aaf2dsv-c1-1.1\n", + " input_source: /data/project/ENG0004504/collection/sgp/sgpaaf2dsF...\n", + " site_id: sgp\n", + " ... ...\n", + " facility_id: F1\n", + " data_level: c1\n", + " location_description: Southern Great Plains (SGP), Gulfstream 159 (\"G1\")...\n", + " datastream: sgpaaf2dsvF1.c1\n", + " doi: 10.5439/1419323\n", + " history: created by user burk on machine prod-proc5.adc.arm..." + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ds_single_1 = xr.load_dataset(\"/data/archive/sgp/sgpaaf2dsvF1.c1/sgpaaf2dsvF1.c1.20160921.163940.nc\")\n", + "ds_single_1\n", + "\n", + "ds_single_2 = xr.load_dataset(\"/data/archive/sgp/sgpaaf2dsvF1.c1/sgpaaf2dsvF1.c1.20160922.160625.nc\")\n", + "ds_single_2" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2 files loaded\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
<xarray.Dataset>\n",
+       "Dimensions:                     (time: 18380, optical_diameter: 61, bound: 2)\n",
+       "Coordinates:\n",
+       "  * time                        (time) datetime64[ns] 2016-09-21T16:39:40 ......\n",
+       "  * optical_diameter            (optical_diameter) float32 10.0 20.0 ... inf\n",
+       "Dimensions without coordinates: bound\n",
+       "Data variables:\n",
+       "    base_time                   (time) datetime64[ns] 2016-09-21 ... 2016-09-22\n",
+       "    time_offset                 (time) datetime64[ns] 2016-09-21T16:39:40 ......\n",
+       "    optical_diameter_bounds     (time, optical_diameter, bound) float32 dask.array<chunksize=(8283, 61, 2), meta=np.ndarray>\n",
+       "    total_number_concentration  (time) float32 dask.array<chunksize=(8283,), meta=np.ndarray>\n",
+       "    number_concentration        (time, optical_diameter) float32 dask.array<chunksize=(8283, 61), meta=np.ndarray>\n",
+       "    lat                         (time) float32 dask.array<chunksize=(8283,), meta=np.ndarray>\n",
+       "    lon                         (time) float32 dask.array<chunksize=(8283,), meta=np.ndarray>\n",
+       "    alt                         (time) float32 dask.array<chunksize=(8283,), meta=np.ndarray>\n",
+       "Attributes: (12/17)\n",
+       "    command_line:          aaf2dsme_ingest -s sgp -f F1 -D -R\n",
+       "    Conventions:           ARM-1.3\n",
+       "    process_version:       ingest-aaf2dsme-1.2-0.el7\n",
+       "    dod_version:           aaf2dsv-c1-1.1\n",
+       "    input_source:          /data/project/ENG0004504/collection/sgp/sgpaaf2dsF...\n",
+       "    site_id:               sgp\n",
+       "    ...                    ...\n",
+       "    doi:                   10.5439/1419323\n",
+       "    history:               created by user burk on machine prod-proc5.adc.arm...\n",
+       "    _file_dates:           ['20160921', '20160922']\n",
+       "    _file_times:           ['163940', '160625']\n",
+       "    _datastream:           sgpaaf2dsvF1.c1\n",
+       "    _arm_standards_flag:   1
" + ], + "text/plain": [ + "\n", + "Dimensions: (time: 18380, optical_diameter: 61, bound: 2)\n", + "Coordinates:\n", + " * time (time) datetime64[ns] 2016-09-21T16:39:40 ......\n", + " * optical_diameter (optical_diameter) float32 10.0 20.0 ... inf\n", + "Dimensions without coordinates: bound\n", + "Data variables:\n", + " base_time (time) datetime64[ns] 2016-09-21 ... 2016-09-22\n", + " time_offset (time) datetime64[ns] 2016-09-21T16:39:40 ......\n", + " optical_diameter_bounds (time, optical_diameter, bound) float32 dask.array\n", + " total_number_concentration (time) float32 dask.array\n", + " number_concentration (time, optical_diameter) float32 dask.array\n", + " lat (time) float32 dask.array\n", + " lon (time) float32 dask.array\n", + " alt (time) float32 dask.array\n", + "Attributes: (12/17)\n", + " command_line: aaf2dsme_ingest -s sgp -f F1 -D -R\n", + " Conventions: ARM-1.3\n", + " process_version: ingest-aaf2dsme-1.2-0.el7\n", + " dod_version: aaf2dsv-c1-1.1\n", + " input_source: /data/project/ENG0004504/collection/sgp/sgpaaf2dsF...\n", + " site_id: sgp\n", + " ... ...\n", + " doi: 10.5439/1419323\n", + " history: created by user burk on machine prod-proc5.adc.arm...\n", + " _file_dates: ['20160921', '20160922']\n", + " _file_times: ['163940', '160625']\n", + " _datastream: sgpaaf2dsvF1.c1\n", + " _arm_standards_flag: 1" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['total_number_concentration', 'number_concentration']" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [ + { + "ename": "ValueError", + "evalue": "x and y arguments to pcolormesh cannot have non-finite values or be of type numpy.ma.core.MaskedArray with masked values", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[0;32mIn[9], line 5\u001b[0m\n\u001b[1;32m 2\u001b[0m ts_display\u001b[38;5;241m.\u001b[39madd_subplots((\u001b[38;5;28mlen\u001b[39m(variables_to_plot),), figsize \u001b[38;5;241m=\u001b[39m (\u001b[38;5;241m9.5\u001b[39m,\u001b[38;5;241m4\u001b[39m\u001b[38;5;241m*\u001b[39m\u001b[38;5;28mlen\u001b[39m(variables_to_plot)))\n\u001b[1;32m 4\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m i,v \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28menumerate\u001b[39m(variables_to_plot):\n\u001b[0;32m----> 5\u001b[0m ts_ax \u001b[38;5;241m=\u001b[39m \u001b[43mts_display\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mplot\u001b[49m\u001b[43m(\u001b[49m\u001b[43mv\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43msubplot_index\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mi\u001b[49m\u001b[43m,\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mset_title\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mds\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mvariables\u001b[49m\u001b[43m[\u001b[49m\u001b[43mv\u001b[49m\u001b[43m]\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mattrs\u001b[49m\u001b[43m[\u001b[49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43mlong_name\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m,\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 6\u001b[0m ts_ax\u001b[38;5;241m.\u001b[39mgrid()\n\u001b[1;32m 8\u001b[0m plt\u001b[38;5;241m.\u001b[39mshow()\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/act/plotting/timeseriesdisplay.py:588\u001b[0m, in \u001b[0;36mTimeSeriesDisplay.plot\u001b[0;34m(self, field, dsname, subplot_index, cmap, set_title, add_nan, day_night_background, invert_y_axis, abs_limits, time_rng, y_rng, use_var_for_y, set_shading, assessment_overplot, overplot_marker, overplot_behind, overplot_markersize, assessment_overplot_category, assessment_overplot_category_color, force_line_plot, labels, cbar_label, cbar_h_adjust, secondary_y, y_axis_flag_meanings, colorbar_labels, cb_friendly, **kwargs)\u001b[0m\n\u001b[1;32m 586\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124medgecolors\u001b[39m\u001b[38;5;124m'\u001b[39m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;129;01min\u001b[39;00m kwargs\u001b[38;5;241m.\u001b[39mkeys():\n\u001b[1;32m 587\u001b[0m kwargs[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124medgecolors\u001b[39m\u001b[38;5;124m'\u001b[39m] \u001b[38;5;241m=\u001b[39m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mface\u001b[39m\u001b[38;5;124m'\u001b[39m\n\u001b[0;32m--> 588\u001b[0m mesh \u001b[38;5;241m=\u001b[39m \u001b[43max\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mpcolormesh\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 589\u001b[0m \u001b[43m \u001b[49m\u001b[43mnp\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43masarray\u001b[49m\u001b[43m(\u001b[49m\u001b[43mxdata\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 590\u001b[0m \u001b[43m \u001b[49m\u001b[43mydata\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 591\u001b[0m \u001b[43m \u001b[49m\u001b[43mdata\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mtranspose\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 592\u001b[0m \u001b[43m \u001b[49m\u001b[43mshading\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mset_shading\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 593\u001b[0m \u001b[43m \u001b[49m\u001b[43mcmap\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcmap\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 594\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 595\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 597\u001b[0m \u001b[38;5;66;03m# Set Title\u001b[39;00m\n\u001b[1;32m 598\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m set_title \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/matplotlib/__init__.py:1442\u001b[0m, in \u001b[0;36m_preprocess_data..inner\u001b[0;34m(ax, data, *args, **kwargs)\u001b[0m\n\u001b[1;32m 1439\u001b[0m \u001b[38;5;129m@functools\u001b[39m\u001b[38;5;241m.\u001b[39mwraps(func)\n\u001b[1;32m 1440\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21minner\u001b[39m(ax, \u001b[38;5;241m*\u001b[39margs, data\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mNone\u001b[39;00m, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs):\n\u001b[1;32m 1441\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m data \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[0;32m-> 1442\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mfunc\u001b[49m\u001b[43m(\u001b[49m\u001b[43max\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;28;43mmap\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43msanitize_sequence\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43margs\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 1444\u001b[0m bound \u001b[38;5;241m=\u001b[39m new_sig\u001b[38;5;241m.\u001b[39mbind(ax, \u001b[38;5;241m*\u001b[39margs, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs)\n\u001b[1;32m 1445\u001b[0m auto_label \u001b[38;5;241m=\u001b[39m (bound\u001b[38;5;241m.\u001b[39marguments\u001b[38;5;241m.\u001b[39mget(label_namer)\n\u001b[1;32m 1446\u001b[0m \u001b[38;5;129;01mor\u001b[39;00m bound\u001b[38;5;241m.\u001b[39mkwargs\u001b[38;5;241m.\u001b[39mget(label_namer))\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/matplotlib/axes/_axes.py:6220\u001b[0m, in \u001b[0;36mAxes.pcolormesh\u001b[0;34m(self, alpha, norm, cmap, vmin, vmax, shading, antialiased, *args, **kwargs)\u001b[0m\n\u001b[1;32m 6217\u001b[0m shading \u001b[38;5;241m=\u001b[39m shading\u001b[38;5;241m.\u001b[39mlower()\n\u001b[1;32m 6218\u001b[0m kwargs\u001b[38;5;241m.\u001b[39msetdefault(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124medgecolors\u001b[39m\u001b[38;5;124m'\u001b[39m, \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mnone\u001b[39m\u001b[38;5;124m'\u001b[39m)\n\u001b[0;32m-> 6220\u001b[0m X, Y, C, shading \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_pcolorargs\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43mpcolormesh\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 6221\u001b[0m \u001b[43m \u001b[49m\u001b[43mshading\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mshading\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mkwargs\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 6222\u001b[0m coords \u001b[38;5;241m=\u001b[39m np\u001b[38;5;241m.\u001b[39mstack([X, Y], axis\u001b[38;5;241m=\u001b[39m\u001b[38;5;241m-\u001b[39m\u001b[38;5;241m1\u001b[39m)\n\u001b[1;32m 6223\u001b[0m \u001b[38;5;66;03m# convert to one dimensional array, except for 3D RGB(A) arrays\u001b[39;00m\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/matplotlib/axes/_axes.py:5717\u001b[0m, in \u001b[0;36mAxes._pcolorargs\u001b[0;34m(self, funcname, shading, *args, **kwargs)\u001b[0m\n\u001b[1;32m 5715\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m funcname \u001b[38;5;241m==\u001b[39m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mpcolormesh\u001b[39m\u001b[38;5;124m'\u001b[39m:\n\u001b[1;32m 5716\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m np\u001b[38;5;241m.\u001b[39mma\u001b[38;5;241m.\u001b[39mis_masked(X) \u001b[38;5;129;01mor\u001b[39;00m np\u001b[38;5;241m.\u001b[39mma\u001b[38;5;241m.\u001b[39mis_masked(Y):\n\u001b[0;32m-> 5717\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\n\u001b[1;32m 5718\u001b[0m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mx and y arguments to pcolormesh cannot have \u001b[39m\u001b[38;5;124m'\u001b[39m\n\u001b[1;32m 5719\u001b[0m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mnon-finite values or be of type \u001b[39m\u001b[38;5;124m'\u001b[39m\n\u001b[1;32m 5720\u001b[0m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mnumpy.ma.core.MaskedArray with masked values\u001b[39m\u001b[38;5;124m'\u001b[39m)\n\u001b[1;32m 5721\u001b[0m \u001b[38;5;66;03m# safe_masked_invalid() returns an ndarray for dtypes other\u001b[39;00m\n\u001b[1;32m 5722\u001b[0m \u001b[38;5;66;03m# than floating point.\u001b[39;00m\n\u001b[1;32m 5723\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(X, np\u001b[38;5;241m.\u001b[39mma\u001b[38;5;241m.\u001b[39mcore\u001b[38;5;241m.\u001b[39mMaskedArray):\n", + "\u001b[0;31mValueError\u001b[0m: x and y arguments to pcolormesh cannot have non-finite values or be of type numpy.ma.core.MaskedArray with masked values" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "b4ce530bc3b34d038b85608090b4b719", + "version_major": 2, + "version_minor": 0 + }, + "image/png": "iVBORw0KGgoAAAANSUhEUgAAA7YAAAMgCAYAAAAXxf7GAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAA9hAAAPYQGoP6dpAAB6jElEQVR4nOzdeXhU5d3/8c+QZbKYjISYTQJERQQDLkEhoAIKQVaVPj9QbACLKA8qIlAFaUughSgq0kJBpIgLWHxaxFqgNLEiQsMmJpVNtIoEJCECIWFNQnL//lBGhyQQYLYT3q/rytWe+3zPmft8Z4jzyTlzxmaMMQIAAAAAwKIa+HoCAAAAAABcDIItAAAAAMDSCLYAAAAAAEsj2AIAAAAALI1gCwAAAACwNIItAAAAAMDSCLYAAAAAAEsj2AIAAAAALI1gCwAAAACwNIItAAAAAMDSCLYAAAAAAEsj2AIAAAAALI1gCwAAAACwNIItAAAAAMDSCLYAAAAAAEsj2AIAAAAALI1gCwAAAACwNIItAAAAAMDSCLYAAAAAAEsj2AIAAAAALI1gCwAAAACwNIItAAAAAMDSCLYAAAAAAEsj2AIAAAAALI1gCwAAAACwNIItAAAAAMDSCLYAAAAAAEsj2AIAAAAALI1gCwAAAACwNIItAAAAAMDSCLYAAAAAAEsj2AIAAAAALI1gCwAAAACwNIItAAAAAMDSCLYAAAAAAEsj2AIAAAAALI1gCwAAAACwNIItAAAAAMDSCLYAAAAAAEsj2AIAAAAALI1gCwAAAACwNIItAAAAAMDSCLYAAAAAAEsj2AIAAAAALI1gCwAAAACwNIItAAAAAMDSCLYAAAAAAEsj2AIAAAAALI1gCwAAAACwNIItAAAAAMDSCLYAAAAAAEsj2AIAAAAALI1gCwAAAACwNIItAAAAAMDSCLYAAAAAAEsj2AIAAAAALI1gCwAAAACwNIItAAAAAMDSCLYAAAAAAEsj2AIAAAAALI1gCwAAAACwNIItAAAAAMDSCLYAAAAAAEsj2AIAAAAALI1gCwAAAACwNIItAAAAAMDSCLYAAAAAAEsj2AIAAAAALI1gCwAAAACwNIItAAAAAMDSCLYAAAAAAEsj2AIAAAAALI1gCwAAAACwNIItAAAAAMDSCLYAAAAAAEsj2AJAPWGz2er089FHH51zX1OnTtV777130fPJyMi4qH34SrNmzdS7d29fT+OSsW/fPmVkZCgvL88j+8/JyVFGRoYOHz5cbV3nzp3VuXNnjzwuAMB7An09AQCAe6xbt85l+be//a1WrVqlDz/80GW8VatW59zX1KlT9T//8z+699573TlFoEb79u3TpEmT1KxZM914441u339OTo4mTZqkIUOG6PLLL3dZN3v2bLc/HgDA+wi2AFBPtG/f3mX5iiuuUIMGDaqNwz8YY3Ty5EmFhob6eiqWc/z4cYWFhbllX3X5Qw8AwP9xKTIAXEIOHTqkESNG6Morr1RwcLCuuuoqTZgwQWVlZc4am82mY8eO6Y033nBevnz6Us3vvvtOI0aMUKtWrXTZZZcpJiZGd955p9asWXNB8/nmm29ks9n04osvavr06UpKStJll12m1NRUrV+/3qW2tktGhwwZombNmlXb5wsvvKDnn39ezZo1U2hoqDp37qwvvvhCFRUVGjdunBISEuRwOHTfffepqKioxvktXbpUbdq0UUhIiK666ir94Q9/qFZTWlqqsWPHKikpScHBwbryyis1atQoHTt2zKXOZrPp8ccf1yuvvKKWLVvKbrfrjTfeOGt/3n77baWmpuqyyy7TZZddphtvvFHz5893qXnttdd0ww03KCQkRFFRUbrvvvu0Y8eOaj267LLL9N///lc9e/bUZZddpsTERI0ZM8bluZeksrIyTZ48WS1btlRISIgaNWqkLl26KCcnx1ljjNHs2bN14403KjQ0VA0bNtT//M//6Ouvv3bZV+fOnZWcnKxNmzbp9ttvV1hYmK666io999xzqqqqkiR99NFHuuWWWyRJDz30kPM1d/oy9tNz37Jli9LS0hQREaG77rpLkpSdna177rlHjRs3VkhIiK655ho9+uijOnDggHMOGRkZ+uUvfylJSkpKqnZJfk2vq7r8O/npc/rWW2+pZcuWCgsL0w033KBly5ad9XkFALgfZ2wB4BJx8uRJdenSRV999ZUmTZqkNm3aaM2aNcrMzFReXp6WL18u6ftLmu+880516dJFv/71ryVJkZGRkr5/wy9JEydOVFxcnI4ePaqlS5eqc+fO+te//nXBn1X84x//qOuuu04zZsyQJP36179Wz549tWvXLjkcjgveZ5s2bfTHP/5Rhw8f1pgxY9SnTx+1a9dOQUFBeu2117R7926NHTtWDz/8sN5//32X7fPy8jRq1ChlZGQoLi5OixYt0pNPPqny8nKNHTtW0vdnDjt16qS9e/fq2WefVZs2bbRt2zb95je/0ZYtW/TBBx/IZrM59/nee+9pzZo1+s1vfqO4uDjFxMTUOv/f/OY3+u1vf6t+/fppzJgxcjgc2rp1q3bv3u2syczM1LPPPqsHHnhAmZmZOnjwoDIyMpSamqpNmzapefPmztqKigr17dtXQ4cO1ZgxY/Txxx/rt7/9rRwOh37zm99Ikk6dOqUePXpozZo1GjVqlO68806dOnVK69evV35+vjp06CBJevTRR/X6669r5MiRev7553Xo0CFNnjxZHTp00H/+8x/FxsY6H7ewsFAPPvigxowZo4kTJ2rp0qUaP368EhISNGjQIN18881asGCBHnroIf3qV79Sr169JEmNGzd27qO8vFx9+/bVo48+qnHjxunUqVOSpK+++kqpqal6+OGH5XA49M0332j69Om67bbbtGXLFgUFBenhhx/WoUOHNHPmTL377ruKj4+XVPuZ2rr+Ozlt+fLl2rRpkyZPnqzLLrtM06ZN03333aedO3fqqquuqvX5BQC4mQEA1EuDBw824eHhzuVXXnnFSDL/93//51L3/PPPG0kmKyvLORYeHm4GDx58zsc4deqUqaioMHfddZe57777XNZJMhMnTjzr9rt27TKSTOvWrc2pU6ec4xs3bjSSzJ///GfnWKdOnUynTp1qPM6mTZtW2+cNN9xgKisrneMzZswwkkzfvn1dth81apSRZEpKSpxjTZs2NTabzeTl5bnUduvWzURGRppjx44ZY4zJzMw0DRo0MJs2bXKp++tf/2okmRUrVrj0w+FwmEOHDp21J8YY8/XXX5uAgADz4IMP1lpTXFxsQkNDTc+ePV3G8/Pzjd1uNwMHDnSODR48uMbnvmfPnqZFixbO5TfffNNIMvPmzav1cdetW2ckmZdeesllfM+ePSY0NNQ8/fTTzrFOnToZSWbDhg0uta1atTLdu3d3Lm/atMlIMgsWLKj2eKfn/tprr9U6J2OMqaqqMhUVFWb37t1Gkvnb3/7mXPfCCy8YSWbXrl3VtjvzdXU+/04kmdjYWFNaWuocKywsNA0aNDCZmZlnnS8AwL24FBkALhEffvihwsPD9T//8z8u40OGDJEk/etf/6rTfl555RXdfPPNCgkJUWBgoIKCgvSvf/2r2uWv56NXr14KCAhwLrdp00aSXM5Onq+ePXuqQYMf/zPXsmVL52P91Onx/Px8l/Hrr79eN9xwg8vYwIEDVVpaqk8//VSStGzZMiUnJ+vGG2/UqVOnnD/du3ev8Q7Ud955pxo2bHjOuWdnZ6uyslKPPfZYrTXr1q3TiRMnnM/faYmJibrzzjurPZ82m019+vRxGWvTpo1Lj//xj38oJCREv/jFL2p93GXLlslms+nnP/+5yzHHxcXphhtuqHbMcXFxuvXWW8/6uHXxs5/9rNpYUVGRhg8frsTEROdrsWnTppJ0wa/H8/130qVLF0VERDiXY2NjFRMTc1GvXQDA+eNSZAC4RBw8eFBxcXEul8ZKUkxMjAIDA3Xw4MFz7mP69OkaM2aMhg8frt/+9reKjo5WQECAfv3rX19UsG3UqJHLst1ulySdOHHigvcZFRXlshwcHHzW8ZMnT7qMx8XFVdvn6bHTvdq/f7/++9//KigoqMY5/PSznpKcl8Gey3fffSfJ9XLcM52eQ037TEhIUHZ2tstYWFiYQkJCXMbsdrvLcX/33XdKSEhw+YPAmfbv3y9jjMvlxj915uW3Zz63px/3fJ7bsLAw5+Xwp1VVVSktLU379u3Tr3/9a7Vu3Vrh4eGqqqpS+/btL/i1c77/TtxxfACAi0ewBYBLRKNGjbRhwwYZY1zetBcVFenUqVOKjo4+5z4WLlyozp07a86cOS7jR44ccft8zxQSEqKSkpJq42eGR3cpLCysdex0mImOjlZoaKhee+21GvdxZk/PDEu1ueKKKyRJe/fuVWJiYo01p+dQUFBQbd2+ffvq9HzW9Lhr165VVVVVreE2OjpaNptNa9ascf4B4qdqGrtYNfVt69at+s9//qPXX39dgwcPdo7/97//vajHcse/EwCA93EpMgBcIu666y4dPXpU7733nsv4m2++6Vx/Wm1nnGw2W7Xg8tlnn1X7Dl1PaNasmb744guXO9MePHjQ5W697rRt2zb95z//cRl7++23FRERoZtvvlmS1Lt3b3311Vdq1KiR2rZtW+3np3drPh9paWkKCAio9geEn0pNTVVoaKgWLlzoMr537159+OGHLs9nXfXo0UMnT57U66+/XmtN7969ZYzRt99+W+Mxt27d+rwf90LO0J8OnWe+HufOnXtR+z+ffycAAP/BGVsAuEQMGjRIf/zjHzV48GB98803at26tdauXaupU6eqZ8+e6tq1q7O2devW+uijj/T3v/9d8fHxioiIUIsWLdS7d2/99re/1cSJE9WpUyft3LlTkydPVlJSkvNOtZ6Snp6uuXPn6uc//7mGDRumgwcPatq0adUuUXWXhIQE9e3bVxkZGYqPj9fChQuVnZ2t559/3vkdqqNGjdKSJUt0xx136KmnnlKbNm1UVVWl/Px8ZWVlacyYMWrXrt15P3azZs307LPP6re//a1OnDihBx54QA6HQ9u3b9eBAwc0adIkXX755fr1r3+tZ599VoMGDdIDDzyggwcPatKkSQoJCdHEiRPP+3EfeOABLViwQMOHD9fOnTvVpUsXVVVVacOGDWrZsqXuv/9+dezYUY888ogeeughffLJJ7rjjjsUHh6ugoICrV27Vq1bt9b//u//ntfjXn311QoNDdWiRYvUsmVLXXbZZUpISFBCQkKt21x33XW6+uqrNW7cOBljFBUVpb///e/VLsGW5Azbv//97zV48GAFBQWpRYsWLp+NPe18/p0AAPwHwRYALhEhISFatWqVJkyYoBdeeEHfffedrrzySo0dO7ZaCPr973+vxx57TPfff7/zK20++ugjTZgwQcePH9f8+fM1bdo0tWrVSq+88oqWLl1a7aZB7taxY0e98cYbeu6553TPPffoqquu0sSJE7VixQqPPPaNN96ohx56SBMnTtSXX36phIQETZ8+XU899ZSzJjw8XGvWrNFzzz2nV199Vbt27VJoaKiaNGmirl27XvAZW0maPHmymjdvrpkzZ+rBBx9UYGCgmjdvrpEjRzprxo8fr5iYGP3hD3/QO++84/y+3qlTp7p81U9dBQYGasWKFcrMzNSf//xnzZgxQxEREbrhhht09913O+vmzp2r9u3ba+7cuZo9e7aqqqqUkJCgjh07VrtRVF2EhYXptdde06RJk5SWlqaKigpNnDjR+V22NQkKCtLf//53Pfnkk3r00UcVGBiorl276oMPPlCTJk1cajt37qzx48frjTfe0Lx581RVVaVVq1bV+PVU5/PvBADgP2zGGOPrSQAAAAAAcKH4jC0AAAAAwNIItgAAAAAASyPYAgAAAAAsjWALAAAAALA0gi0AAAAAwNIItgAAAAAAS+N7bOupqqoq7du3TxEREbLZbL6eDgAAAFCvGWN05MgRJSQkqEED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SVlZWdq+fbsWLlyom266SV27dtVLL72kefPmqbS01PtNAQAAAAA/R7D9iccee0y9evVS165dXcZ37dqlwsJCpaWlOcfsdrs6deqknJwcSdLmzZtVUVHhUpOQkKDk5GRnzbp16+RwONSuXTtnTfv27eVwOFxqkpOTlZCQ4Kzp3r27ysrKtHnzZvcfNAAAAABYXKCvJ+AuFRUVKiws1PHjx3XFFVcoKirqvLZfvHixPv30U23atKnausLCQklSbGysy3hsbKx2797trAkODnY503u65vT2hYWFiomJqbb/mJgYl5ozH6dhw4YKDg521tSkrKxMZWVlzuXTZ3crKipUUVFR63bedHoe5zOfU2fWGuM3x+NrF9JPnB09dS/66V70073op3vRT/ein+7lrX7yfPmWpYPt0aNHtWjRIv35z3/Wxo0bXYJd48aNlZaWpkceeUS33HLLWfezZ88ePfnkk8rKylJISEitdTabzWXZGFNt7Exn1tRUfyE1Z8rMzNSkSZOqjWdlZSksLOysc/S27OzsOtcerZB++jItKyvTihUr3D8pCzuffqJu6Kl70U/3op/uRT/di366F/10L0/38/jx4x7dP87OssH25Zdf1pQpU9SsWTP17dtX48aN05VXXqnQ0FAdOnRIW7du1Zo1a9StWze1b99eM2fOVPPmzWvc1+bNm1VUVKSUlBTnWGVlpT7++GPNmjXL+fnXwsJCxcfHO2uKioqcZ1fj4uJUXl6u4uJil7O2RUVF6tChg7Nm//791R7/u+++c9nPhg0bXNYXFxeroqKi2pncnxo/frxGjx7tXC4tLVViYqLS0tIUGRlZ63beVFFRoezsbHXr1k1BQUF12ubgsXJN+OQj57LdblfPnp09M0GLuZB+4uzoqXvRT/ein+5FP92LfroX/XQvb/WT++H4lmWDbU5OjlatWqXWrVvXuP7WW2/VL37xC82ZM0evvfaaVq9eXWuwveuuu7RlyxaXsYceekjXXXednnnmGV111VWKi4tTdna2brrpJklSeXm5Vq9ereeff16SlJKSoqCgIGVnZ6t///6SpIKCAm3dulXTpk2TJKWmpqqkpEQbN27UrbfeKknasGGDSkpKnOE3NTVVU6ZMUUFBgTNEZ2VlyW63uwTvM9ntdtnt9mrjQUFBfvcL8XzmFBRY5Tpgs/nd8fiaPz7HVkdP3Yt+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s9n03nvvuaw/evSoHn/8cTVu3FihoaFq2bKl5syZc879btmyRZ06dVJoaKiuvPJKTZ48WWfez2z16tVKSUlRSEiIrrrqKr3yyivuPDSvy8zM1C233KKIiAjFxMTo3nvv1c6dO11q3n33XXXv3l3R0dGy2WzKy8ur074vxX6eNnv2bCUlJSkkJEQpKSlas2aNc50xRhkZGUpISFBoaKg6d+6sbdu2nXOfl3I/pbP3NCMjQ9ddd53Cw8PVsGFDde3aVRs2bDjnPi/lnp6tn5K0Y8cO9e3bVw6HQxEREWrfvr3y8/PPuk/6WXM/9+/fryFDhighIUFhYWG6++679eWXX55zn5dqP8/23/iKigo988wzat26tcLDw5WQkKBBgwZp375959wv/az5PdOQIUNks9lcftq3b3/O/dJP3oPiHAxQBytWrDATJkwwS5YsMZLM0qVLXdY//PDD5uqrrzarVq0yu3btMnPnzjUBAQHmvffeq3WfJSUlJjY21tx///1my5YtZsmSJSYiIsK8+OKLzpqvv/7ahIWFmSeffNJs377dzJs3zwQFBZm//vWvnjpUj+vevbtZsGCB2bp1q8nLyzO9evUyTZo0MUePHnXWvPnmm2bSpElm3rx5RpLJzc09534v1X4aY8zixYtNUFCQmTdvntm+fbt58sknTXh4uNm9e7cxxpjnnnvOREREmCVLlpgtW7aYAQMGmPj4eFNaWlrrPi/lfhpz7p4uWrTIZGdnm6+++sps3brVDB061ERGRpqioqJa93kp9/Rc/fzvf/9roqKizC9/+Uvz6aefmq+++sosW7bM7N+/v9Z90s+a+1lVVWXat29vbr/9drNx40bz+eefm0ceeaTa79kzXcr9PNt/4w8fPmy6du1q3nnnHfP555+bdevWmXbt2pmUlJSz7pN+1v6eafDgwebuu+82BQUFzp+DBw+edZ/0k/egODeCLc5bTb9Urr/+ejN58mSXsZtvvtn86le/qnU/s2fPNg6Hw5w8edI5lpmZaRISEkxVVZUxxpinn37aXHfddS7bPfroo6Z9+/YXeRT+o6ioyEgyq1evrrZu165ddQ62l3I/b731VjN8+HCXseuuu86MGzfOVFVVmbi4OPPcc8851508edI4HA7zyiuv1LrPS7mfxpy9pzUpKSkxkswHH3xQ6z4v5Z6eq58DBgwwP//5z89rn/Sz5n7u3LnTSDJbt251rjt16pSJiooy8+bNq3Wfl3I/f6qm/8afaePGjUaS8w8zNaGf36st2N5zzz3ntR/6+T3eg+JsuBQZbnHbbbfp/fff17fffitjjFatWqUvvvhC3bt3d9YMGTJEnTt3di6vW7dOnTp1cvmy7O7du2vfvn365ptvnDVpaWkuj9W9e3d98sknqqio8OgxeUtJSYkkKSoq6ry2o5/fKy8v1+bNm6sdV1pamnJycrRr1y4VFha6rLfb7erUqZNycnKcY/TzR+fqaU31r776qhwOh2644QbnOD393rn6WVVVpeXLl+vaa69V9+7dFRMTo3bt2tV4+SL9PHc/y8rKJEkhISHOdQEBAQoODtbatWudY/TzwpWUlMhms+nyyy93jtHP8/PRRx8pJiZG1157rYYNG6aioiKX9fSz7ngPitMItnCLP/zhD2rVqpUaN26s4OBg3X333Zo9e7Zuu+02Z018fLyaNGniXC4sLFRsbKzLfk4vFxYWnrXm1KlTOnDggKcOx2uMMRo9erRuu+02JScnn9e29PN7Bw4cUGVlZY3HVVhY6Dz22tafRj9/dK6enrZs2TJddtllCgkJ0csvv6zs7GxFR0c719PT752rn0VFRTp69Kiee+453X333crKytJ9992nfv36afXq1c56+vm9c/XzuuuuU9OmTTV+/HgVFxervLxczz33nAoLC1VQUOCsp58X5uTJkxo3bpwGDhyoyMhI5zj9rLsePXpo0aJF+vDDD/XSSy9p06ZNuvPOO51/lJHo5/ngPShOC/T1BFA//OEPf9D69ev1/vvvq2nTpvr44481YsQIxcfHq2vXrpK+v2nSmWw2m8uy+eFD+z8dr0uNVT3++OP67LPPXM4i1BX9dFXTcZ3ruH86Rj+rO1fPunTpory8PB04cEDz5s1T//79tWHDBsXExEiip2eqrZ9VVVWSpHvuuUdPPfWUJOnGG29UTk6OXnnlFXXq1EkS/TxTbf0MCgrSkiVLNHToUEVFRSkgIEBdu3ZVjx49XOrp5/mrqKjQ/fffr6qqKs2ePdtlHf2suwEDBjj/f3Jystq2baumTZtq+fLl6tevnyT6eT54D4rTCLa4aCdOnNCzzz6rpUuXqlevXpKkNm3aKC8vTy+++KLzl8qZ4uLiXM7+SHJeinP6L2S11QQGBqpRo0buPhSveuKJJ/T+++/r448/VuPGjS96f5dqP6OjoxUQEFDjccXGxiouLk7S9395jY+Pr7a+NpdqP6Vz9/S08PBwXXPNNbrmmmvUvn17NW/eXPPnz9f48eNr3O+l2tNz9TM6OlqBgYFq1aqVy/qWLVue9Y9e9LP212dKSory8vJUUlKi8vJyXXHFFWrXrp3atm1b634v1X7WVUVFhfr3769du3bpww8/dDlbWxP6WXfx8fFq2rTpWe/cTT9rxntQ/BSXIuOiVVRUqKKiQg0auL6cAgICnGciapKamqqPP/5Y5eXlzrGsrCwlJCSoWbNmzprs7GyX7bKystS2bVsFBQW57yC8yBijxx9/XO+++64+/PBDJSUluWW/l2o/g4ODlZKSUu24srOz1aFDByUlJSkuLs5lfXl5uVavXq0OHTrUut9LtZ/SuXtaG2OMy6V0Z7pUe3qufgYHB+uWW26p9rVfX3zxhZo2bVrrfunnuV+fDodDV1xxhb788kt98sknuueee2rd76Xaz7o4HWq//PJLffDBB3V6U08/6+7gwYPas2ePyx9fz0Q/a8Z7ULjwzj2qYHVHjhwxubm5Jjc310gy06dPN7m5uc47Inbq1Mlcf/31ZtWqVebrr782CxYsMCEhIWb27NnOfYwbN86kp6c7lw8fPmxiY2PNAw88YLZs2WLeffddExkZWeOt1p966imzfft2M3/+fMvfav1///d/jcPhMB999JHLrf6PHz/urDl48KDJzc01y5cvN5LM4sWLTW5urikoKHDW0M8fnf7qj/nz55vt27ebUaNGmfDwcPPNN98YY77/uh+Hw2Heffdds2XLFvPAAw9U+7of+unqbD09evSoGT9+vFm3bp355ptvzObNm83QoUON3W53uRMtPf3RuV6j7777rgkKCjKvvvqq+fLLL83MmTNNQECAWbNmjXMf9PNH5+rn//3f/5lVq1aZr776yrz33numadOmpl+/fi77oJ8/Ott/4ysqKkzfvn1N48aNTV5enst/t8rKypz7oJ8/Ols/jxw5YsaMGWNycnLMrl27zKpVq0xqaqq58sor+W9SLXgPiroi2KJOVq1aZSRV+xk8eLAxxpiCggIzZMgQk5CQYEJCQkyLFi3MSy+95LxlujHf396+U6dOLvv97LPPzO23327sdruJi4szGRkZLtsYY8xHH31kbrrpJhMcHGyaNWtm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07nRo7qhT+dGj+qGPvkW32MLAAAAALA0LkUGAAAAAFgawRYAAAAAYGkEWwAAAACApRFsAQAAAACWRrD1U5mZmbrlllsUERGhmJgY3Xvvvdq5c6dLjTFGGRkZSkhIUGhoqDp37qxt27a51JSVlemJJ55QdHS0wsPD1bdvX+3du9elpri4WOnp6XI4HHI4HEpPT9fhw4c9fYhu4c0+TZkyRR06dFBYWJguv/xyTx+a23irR998842GDh2qpKQkhYaG6uqrr9bEiRNVXl7uleO8WN58LfXt21dNmjRRSEiI4uPjlZ6ern379nn8GC+WN3v009obb7xRNptNeXl5njo0t/Jmn5o1ayabzebyM27cOI8fozt4+/W0fPlytWvXTqGhoYqOjla/fv08enzu4K0effTRR9VeR6d/Nm3a5JVjvRjefC198cUXuueeexQdHa3IyEh17NhRq1at8vgxuoM3+/Tpp5+qW7duuvzyy9WoUSM98sgjOnr0qMeP8WK5q0evvvqqOnfurMjISNlsthrfV1v5/bffMvBL3bt3NwsWLDBbt241eXl5plevXqZJkybm6NGjzprnnnvOREREmCVLlpgtW7aYAQMGmPj4eFNaWuqsGT58uLnyyitNdna2+fTTT02XLl3MDTfcYE6dOuWsufvuu01ycrLJyckxOTk5Jjk52fTu3durx3uhvNmn3/zmN2b69Olm9OjRxuFwePMwL4q3evSPf/zDDBkyxPzzn/80X331lfnb3/5mYmJizJgxY7x+zBfCm6+l6dOnm3Xr1plvvvnG/Pvf/zapqakmNTXVq8d7IbzZo9NGjhxpevToYSSZ3NxcbxzmRfNmn5o2bWomT55sCgoKnD9Hjhzx6vFeKG/26a9//atp2LChmTNnjtm5c6f5/PPPzV/+8hevHu+F8FaPysrKXF5DBQUF5uGHHzbNmjUzVVVVXj/u8+XN19I111xjevbsaf7zn/+YL774wowYMcKEhYWZgoICrx7zhfBWn7799lvTsGFDM3z4cPP555+bjRs3mg4dOpif/exnXj/m8+WuHr388ssmMzPTZGZmGkmmuLi42mNZ+f23vyLYWkRRUZGRZFavXm2MMaaqqsrExcWZ5557zllz8uRJ43A4zCuvvGKMMebw4cMmKCjILF682Fnz7bffmgYNGpiVK1caY4zZvn27kWTWr1/vrFm3bp2RZD7//HNvHJpbeapPP7VgwQJLBdszeaNHp02bNs0kJSV56Eg8y5t9+tvf/mZsNpspLy/30NF4hqd7tGLFCnPdddeZbdu2WSrYnsmTfWratKl5+eWXvXMgHuapPlVUVJgrr7zS/OlPf/Li0XiGt34vlZeXm5iYGDN58mQPHo3neKpP3333nZFkPv74Y2dNaWmpkWQ++OADbxyaW3mqT3PnzjUxMTGmsrLSWZObm2skmS+//NIbh+Y2F9Kjn1q1alWNwba+vf/2F1yKbBElJSWSpKioKEnSrl27VFhYqLS0NGeN3W5Xp06dlJOTI0navHmzKioqXGoSEhKUnJzsrFm3bp0cDofatWvnrGnfvr0cDoezxko81af6xJs9KikpcT6O1XirT4cOHdKiRYvUoUMHBQUFeepwPMKTPdq/f7+GDRumt956S2FhYd44HI/x9Gvp+eefV6NGjXTjjTdqypQplrn8/0ye6tOnn36qb7/9Vg0aNNBNN92k+Ph49ejRo9qlg1bgrd9L77//vg4cOKAhQ4Z46Eg8y1N9atSokVq2bKk333xTx44d06lTpzR37lzFxsYqJSXFW4fnNp7qU1lZmYKDg9WgwY8xIzQ0VJK0du1azx6Um11Ij+qivr3/9hcEWwswxmj06NG67bbblJycLEkqLCyUJMXGxrrUxsbGOtcVFhYqODhYDRs2PGtNTExMtceMiYlx1liFJ/tUX3izR1999ZVmzpyp4cOHu/swPM4bfXrmmWcUHh6uRo0aKT8/X3/72988dTge4ckeGWM0ZMgQDR8+XG3btvX0oXiUp19LTz75pBYvXqxVq1bp8ccf14wZMzRixAhPHpJHeLJPX3/9tSQpIyNDv/rVr7Rs2TI1bNhQnTp10qFDhzx6XO7kzd/f8+fPV/fu3ZWYmOjuw/A4T/bJZrMpOztbubm5ioiIUEhIiF5++WWtXLnSUvfekDzbpzvvvFOFhYV64YUXVF5eruLiYj377LOSpIKCAo8elztdaI/qoj69//YnBFsLePzxx/XZZ5/pz3/+c7V1NpvNZdkYU23sTGfW1FRfl/34G0/3qT7wVo/27dunu+++W//v//0/Pfzwwxc3aR/wRp9++ctfKjc3V1lZWQoICNCgQYNkjLn4yXuJJ3s0c+ZMlZaWavz48e6bsI94+rX01FNPqVOnTmrTpo0efvhhvfLKK5o/f74OHjzongPwEk/2qaqqSpI0YcIE/exnP1NKSooWLFggm82mv/zlL246As/z1u/vvXv36p///KeGDh16cRP2EU/2yRijESNGKCYmRmvWrNHGjRt1zz33qHfv3pYKbJJn+3T99dfrjTfe0EsvvaSwsDDFxcXpqquuUmxsrAICAtx3EB7m7h6dax8Xuh/8iGDr55544gm9//77WrVqlRo3buwcj4uLk6Rqf9UpKipy/hUpLi7O+Zeys9Xs37+/2uN+99131f4a5c883af6wFs92rdvn7p06aLU1FS9+uqrnjgUj/JWn6Kjo3XttdeqW7duWrx4sVasWKH169d74pDcztM9+vDDD7V+/XrZ7XYFBgbqmmuukSS1bdtWgwcP9thxuZsvfi+1b99ekvTf//7XLcfgDZ7uU3x8vCSpVatWzvV2u11XXXWV8vPz3X9AHuDN19KCBQvUqFEj9e3b192H4XHe+N20bNkyLV68WB07dtTNN9+s2bNnKzQ0VG+88YYnD82tvPF6GjhwoAoLC/Xtt9/q4MGDysjI0HfffaekpCRPHZZbXUyP6qK+vP/2Ox79BC8uWFVVlXnsscdMQkKC+eKLL2pcHxcXZ55//nnnWFlZWY0f8H/nnXecNfv27avx5lEbNmxw1qxfv94yH173Vp9+ymo3j/Jmj/bu3WuaN29u7r///hrvcOvPfPFaOi0/P99IMqtWrXLfAXmAt3q0e/dus2XLFufPP//5TyPJ/PWvfzV79uzx8FFePF++lv7+978bSWb37t1uPCLP8FafSkpKjN1ud7l51OmbI82dO9dTh+cW3n4tVVVVmaSkJMvczf40b/Xp/fffNw0aNKh25/Frr73WTJkyxROH5la+/N00f/58ExYWVuPdgf2JO3r0U+e6eZRV33/7K4Ktn/rf//1f43A4zEcffeRy+/3jx487a5577jnjcDjMu+++a7Zs2WIeeOCBGm/J3rhxY/PBBx+YTz/91Nx55501ft1PmzZtzLp168y6detM69atLXO7cW/2affu3SY3N9dMmjTJXHbZZSY3N9fk5ub6/VdreKtH3377rbnmmmvMnXfeafbu3evyWFbgrT5t2LDBzJw50+Tm5ppvvvnGfPjhh+a2224zV199tTl58qTXj/t8ePPf20/t2rXLUndF9lafcnJyzPTp001ubq75+uuvzTvvvGMSEhJM3759vX7MF8Kbr6cnn3zSXHnlleaf//yn+fzzz83QoUNNTEyMOXTokFeP+Xx5+9/cBx98YCSZ7du3e+0Y3cFbffruu+9Mo0aNTL9+/UxeXp7ZuXOnGTt2rAkKCjJ5eXleP+7z5c3X08yZM83mzZvNzp07zaxZs0xoaKj5/e9/79XjvRDu6lFBQYHJzc018+bNc95JOzc31xw8eNBZY+X33/6KYOunJNX4s2DBAmdNVVWVmThxoomLizN2u93ccccdZsuWLS77OXHihHn88cdNVFSUCQ0NNb179zb5+fkuNQcPHjQPPvigiYiIMBEREebBBx/0+7+onebNPg0ePLjGx/L3s2ze6tGCBQtqfSwr8FafPvvsM9OlSxcTFRVl7Ha7adasmRk+fLjZu3evtw71gnnz39tPWS3YeqtPmzdvNu3atTMOh8OEhISYFi1amIkTJ5pjx45561AvijdfT+Xl5WbMmDEmJibGREREmK5du5qtW7d64zAvirf/zT3wwAOmQ4cOnj4st/NmnzZt2mTS0tJMVFSUiYiIMO3btzcrVqzwxmFeNG/2KT093URFRZng4GDTpk0b8+abb3rjEC+au3o0ceLEc+7Hyu+//ZXNGAvdrQQAAAAAgDNw8ygAAAAAgKURbAEAAAAAlkawBQAAAABYGsEWAAAAAGBpBFsAAAAAgKURbAEAAAAAlkawBQAAAABYGsEWAAAAAGBpBFsAAAAAgKURbAEAAAAAlkaw/f/t1wEJAAAAgKD/r9sR6AsBAABYE1sAAADWxBYAAIA1sQUAAGBNbAEAAFgTWwAAANbEFgAAgDWxBQAAYE1sAQAAWBNbAAAA1sQWAACANbEFAABgTWwBAABYE1sAAADWxBYAAIA1sQUAAGBNbAEAAFgTWwAAANbEFgAAgDWxBQAAYE1sAQAAWBNbAAAA1sQWAACANbEFAABgTWwBAABYE1sAAADWxBYAAIA1sQUAAGBNbAEAAFgTWwAAANbEFgAAgDWxBQAAYE1sAQAAWBNbAAAA1sQWAACANbEFAABgTWwBAABYE1sAAADWxBYAAIA1sQUAAGBNbAEAAFgTWwAAANbEFgAAgDWxBQAAYE1sAQAAWBNbAAAA1sQWAACANbEFAABgTWwBAABYE1sAAADWxBYAAIA1sQUAAGBNbAEAAFgTWwAAANbEFgAAgDWxBQAAYC1l8f/E0y7UQgAAAABJRU5ErkJggg==", + "text/html": [ + "\n", + "
\n", + "
\n", + " Figure\n", + "
\n", + " \n", + "
\n", + " " + ], + "text/plain": [ + "Canvas(toolbar=Toolbar(toolitems=[('Home', 'Reset original view', 'home', 'home'), ('Back', 'Back to previous …" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'total_number_concentration'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/2DS-AIR/2DS-AIR_tutorial.ipynb b/VAPs/quicklook/2DS-AIR/2DS-AIR_tutorial.ipynb new file mode 100644 index 00000000..bcefbb2f --- /dev/null +++ b/VAPs/quicklook/2DS-AIR/2DS-AIR_tutorial.ipynb @@ -0,0 +1,838 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# AAF2DSH.C1 Notebook\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/2ds-air) for more information about this vap." + ] + }, + { + "cell_type": "markdown", + "id": "97097763", + "metadata": {}, + "source": [ + "In this notebook, we demonstrate the workflow to explore ARM vap data (using aaf2dsh as an example.) Value-added products (VAPs) are higher-order data products that have been analyzed and processed to ease scientists’ use of ARM data in atmospheric research and global climate models. \n", + "Here is the main content we will cover." + ] + }, + { + "cell_type": "markdown", + "id": "eddec40f", + "metadata": {}, + "source": [ + "# Table of Content\n", + "## Access the data\n", + "* How to retrieve the data\n", + "* Data path and file name conventions\n", + "* Load data\n", + "## Explore the data\n", + "* NetCDF Data structure\n", + "* Xarray essentials\n", + "* Xarray Variable\n", + "## Plot the data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "import random\n", + "\n", + "import glob\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "2372d5be", + "metadata": {}, + "source": [ + "## Access the data" + ] + }, + { + "cell_type": "markdown", + "id": "5b0d9684", + "metadata": {}, + "source": [ + "### How to retrieve the data\n", + "We assume the path \"/data/archive\" is available where you are running this notebook. You can use `os.path.exists(\"/data/archive\")` to verify if the path exists at your machine.\n", + "\n", + "\n", + "### Data path and file name conventions\n", + "There several common terminologies regarding ARM data, for example, data-stream-name, data-level, site, facility, instrument, etc. (For more details, please see [ARM Data File Standards Version 1.2](https://www.google.com/search?q=arm+datastream+facility+cite+definition&rlz=1C1GCEJ_enUS1029US1029&ei=hb41ZICrBPukqtsPvMODIA&ved=0ahUKEwjAgeiS0aL-AhV7kmoFHbzhAAQQ4dUDCBA&uact=5&oq=arm+datastream+facility+cite+definition&gs_lcp=Cgxnd3Mtd2l6LXNlcnAQAzIFCAAQogQyBQgAEKIEMgUIABCiBDIFCAAQogQyBQgAEKIEOgoIABBHENYEELADOgoIIRCgARDDBBAKSgQIQRgAUJ4DWIoOYO0PaAFwAXgAgAGMAYgBuQmSAQMyLjmYAQCgAQHIAQjAAQE&sclient=gws-wiz-serp).) \n", + "\n", + "For example, this notebook is called `aaf2dsh.c1`, where `aaf2dsh` is the \"datastream name\", and the `{process.ds_class_level}}` is the \"data level\".\n", + "\n", + "This datastream also contains site `cor` and facility `F1`. (Note: individual datastream might have multiple site-facility pairs.)\n", + "In such a case, the data of this data-stream is stored at `/data/archive/cor/coraaf2dshF1.c1`, which is in the format of `//.`. We can use the following method to assign the data-stream directory `datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )`\n", + "\n", + "The data files under datastream_dir also follows naming conventions. But once reach the datastream_dir level, the most import file naming convention to differentiate the files is \"yyyyMMdd.hhmmss\", which comes handy to filter out files based on datetime. For example, we can use `glob.glob(f'{datastream_dir}/*.200709*.*')` to filter files in 2007 September.\n", + "\n", + "(Note: refer to the https://adc.arm.gov/discovery/#/ and https://adc.arm.gov/solr8/metadata/select API to explore ARM datastream and assoicated available sites and facilities\n", + "\n", + "Please see the following examples in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d7e9eb85", + "metadata": {}, + "outputs": [], + "source": [ + "# Verify if DATA_DIR path exists\n", + "DATA_DIR = \"/data/archive\"\n", + "os.path.exists(DATA_DIR)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "586993fd", + "metadata": {}, + "outputs": [], + "source": [ + "# Speicify datastream_dir following the path conventions and check its existence\n", + "DATASTREAM_NAME = \"aaf2dsh\"\n", + "DATA_LEVEL = \"c1\"\n", + "site = \"cor\"\n", + "facility = \"F1\"\n", + "datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )\n", + "print(datastream_dir)\n", + "print(os.path.exists(datastream_dir))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0742f7c1", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: list 5 (random) files under datastream_dir\n", + "files = os.listdir(datastream_dir)\n", + "files[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39b98a36", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: get most recent file\n", + "list_of_files = glob.glob(f\"{datastream_dir}/*\") # * means all if need specific format then *.csv\n", + "latest_file = max(list_of_files, key=os.path.getctime)\n", + "latest_file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "902d514e", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus sort datastream files based on datetime\n", + "files = os.listdir(datastream_dir)\n", + "file_sorted = files.copy()\n", + "file_sorted.sort() \n", + "print(file_sorted[:5])\n", + "\n", + "# to reverse\n", + "file_sorted_reverse = files.copy() \n", + "file_sorted_reverse.sort(reverse=True)\n", + "print(file_sorted_reverse[:5])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec5923b2", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: pattern matching\n", + "# filter the 200709** files under datastream_dir\n", + "files_filter = glob.glob(f'{datastream_dir}/*.200709*.*')\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c4a4aa18", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: use armnotebook_utils.file_filter (TODO) to filter files based on datastream info \n" + ] + }, + { + "cell_type": "markdown", + "id": "cfeb9efc", + "metadata": {}, + "source": [ + "### Load data\n", + "The arm data is stored in [NetCDF](https://en.wikipedia.org/wiki/NetCDF#:~:text=%22NetCDF%20(network%20Common%20Data%20Form,format%20for%20representing%20scientific%20data.) format. We can use xarray's `open_dataset` method to load single file, or `open_mfdataset` to open multiple files. (Note: the latter will still return a single xarray dataset object by combining multiple files.)\n", + "\n", + "Note: open_dataset keeps the file handle open and lazy loads its contents. All parameters are passed directly to open_dataset. It is a preferable method over load_dataset for memory-effiency. (more details, please see [xarray.open_dataset](https://docs.xarray.dev/en/stable/generated/xarray.open_dataset.html))\n", + "\n", + "See the following example in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a440a329", + "metadata": {}, + "outputs": [], + "source": [ + "# open single file\n", + "full_path = latest_file\n", + "print(full_path)\n", + "ds_single = xr.open_dataset(full_path)\n", + "ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0143a3d", + "metadata": {}, + "outputs": [], + "source": [ + "print(ds_single)\n", + "print(type(ds_single))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c0f8939", + "metadata": {}, + "outputs": [], + "source": [ + "# open multiple files\n", + "n_files = 3\n", + "full_paths = [os.path.join(datastream_dir, f_path) for f_path in file_sorted_reverse[:n_files]]\n", + "print(full_paths)\n", + "try: # Note: sometimes multiple files cannot be merged, so we used try except here.\n", + " ds_mutiple = xr.open_mfdataset(full_paths)\n", + " # ds_mutiple\n", + " print(type(ds_mutiple))\n", + " print(ds_mutiple)\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "3fa59943", + "metadata": {}, + "source": [ + "## Explore the data" + ] + }, + { + "cell_type": "markdown", + "id": "571f69b9", + "metadata": {}, + "source": [ + "### NetCDF Data structure \n", + "\n", + "\n", + "(If you are confident with NetCDF basics and xarray essentials, feel free to skip this session.)\n", + "Before we dive into data exploration, there are some eseential concpets we should be familiar with (shown below): \n", + "* Dataset\n", + "* Data array\n", + "* Variable\n", + "* Dimenssion\n", + "* Coordinate\n", + "* Data Type\n", + "* Meta Data (Attributes)\n", + "\n", + "We will not go into details about NetCDF basics and here are some references you might find helpful\n", + "* [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#)\n", + "* [Network Common Data Form (NetCDF)](https://www.unidata.ucar.edu/software/netcdf/) and [A Brief History of (netCDF) Time](https://www.unidata.ucar.edu/software/netcdf/time/recs.html)\n", + "\n", + "\n", + "\n", + "### Xarray essentials\n", + "Earlier we introduced the xarray pacakge and used `open_dataset` and `open_mfdataset` to retrieve NetCDF data file as an xarray Dataset object. Recall that you can review the data in a notebook by using `print(ds)` or `ds`. \n", + "\n", + "Using xarray to retrieve the aforementioned NetCDF basics is straightforwared. In general,\n", + "* Dataset: `ds`\n", + "* Data array: `ds.variables`\n", + "* Variable: `ds.ds.variables`\n", + "* Dimenssion: `ds.dims`\n", + "* Coordinate: `ds.coords`\n", + "* Data Type: `type`\n", + "* Meta Data (Attributes)\n", + "\n", + "Also, here are some references if you are new to xarray\n", + "* [Xarray in 45 minutes](https://tutorial.xarray.dev/overview/xarray-in-45-min.html)\n", + "* [Handling NetCDF Files using XArray for Absolute Beginners](https://towardsdatascience.com/handling-netcdf-files-using-xarray-for-absolute-beginners-111a8ab4463f)\n", + "\n", + "Try the following commands in action\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b3eac323", + "metadata": {}, + "outputs": [], + "source": [ + "ds = ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77ecf85d", + "metadata": {}, + "outputs": [], + "source": [ + "# Dataset \n", + "ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25e7de09", + "metadata": {}, + "outputs": [], + "source": [ + "# Data array, Variable\n", + "# Note: the info can be overwhelming. Do not attempt to grasp everything at the first glance.\n", + "ds.variables" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c41b67e", + "metadata": {}, + "outputs": [], + "source": [ + "# dimenssions\n", + "ds.dims" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "156f1dfc", + "metadata": {}, + "outputs": [], + "source": [ + "# coordinates\n", + "ds.coords" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "277d6064", + "metadata": {}, + "outputs": [], + "source": [ + "# Meta Data (Attributes)\n", + "ds.attrs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d334681f", + "metadata": {}, + "outputs": [], + "source": [ + "# type\n", + "print(type(ds))\n", + "print(type(ds.variables))\n", + "print(type(ds.dims))\n", + "print(type(ds.coords))\n", + "print(type(ds.attrs))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "643399d6", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: retrieve variable names only\n", + "list(ds.variables)" + ] + }, + { + "cell_type": "markdown", + "id": "1cf36878", + "metadata": {}, + "source": [ + "#### Discussion: variable vs. coordinates vs. dimenssions. \n", + "From [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#):\n", + "\n", + "* Dimession: A dimension may be used to represent a real physical dimension, for example, time, latitude, longitude, or height. A dimension might also be used to index other quantities, for example station or model-run-number.\n", + "A netCDF dimension has both a name and a length. A dimension length is an arbitrary positive integer, except that one dimension in a netCDF dataset can have the length UNLIMITED.\n", + "\n", + "* Variables: Variables are used to store the bulk of the data in a netCDF dataset. A variable represents an array of values of the same type. A scalar value is treated as a 0-dimensional array. A variable has a name, a data type, and a shape described by its list of dimensions specified when the variable is created. A variable may also have associated attributes, which may be added, deleted or changed after the variable is created.\n", + "\n", + "* Coordinate (Variables): It is legal for a variable to have the same name as a dimension. Such variables have no special meaning to the netCDF library. However there is a convention that such variables should be treated in a special way by software using this library.\n", + "A variable with the same name as a dimension is called a coordinate variable. It typically defines a physical coordinate corresponding to that dimension.\n", + "\n", + "\n", + "Tips: By definition, coordinates and dimenssions are also variable. For example ds.time is a coordinate but it is also a special variable. But in practice, when we talk about variable, it implies regular/non-coordinate variable. We will use this convention for the remaining notebook. " + ] + }, + { + "cell_type": "markdown", + "id": "d47120f9", + "metadata": {}, + "source": [ + "### Xarray Variable\n", + "\n", + "\n", + "We can use `ds.variables` to access varialbes of a dataset. But many times viewing print-out the whole datasets can be overwhemling. Instead, we would work on the individual variable (also called Xarray data array.) using the `ds[var_name]` syntax, where `var_name` is the name of the variable. See the following examples in action.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5a57e136", + "metadata": {}, + "outputs": [], + "source": [ + "print(type(ds[\"time\"]))\n", + "ds[\"time\"]" + ] + }, + { + "cell_type": "markdown", + "id": "51ec643e", + "metadata": {}, + "source": [ + "#### Variable properties\n", + "For individual varible, the following properties are mostly used with the assocaited xarray method to retrieve them. (assuming var = ds[var_name])\n", + "* name: `var.name`\n", + "* data content: `var.data`\n", + "* attributes: `var.attrs`\n", + "* dimenstions: `var.dims`\n", + "* data type: `var.data.dtype`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a632a525", + "metadata": {}, + "outputs": [], + "source": [ + "var_name = \"time\"\n", + "var = ds[var_name]\n", + "\n", + "print(\"var.name: \\n\", var.name, \"\\n\")\n", + "print(\"var.data: \\n\", var.data, \"\\n\")\n", + "print(\"var.attrs: \\n\", var.attrs, \"\\n\")\n", + "print(\"var.dims: \\n\", var.dims, \"\\n\")\n", + "print(\"var.data.dtype: \\n\", var.data.dtype, \"\\n\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b256b07f", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: indivdual variable info table (collect and display variable info as a tabular format)\n", + "df_info = pd.DataFrame()\n", + "df_info[\"var_name\"] = list(ds.variables)\n", + "df_info[\"dims\"] = df_info.var_name.apply(lambda x: ds[x].dims)\n", + "df_info[\"is_dim\"] = df_info.var_name.apply(lambda x: x in ds.dims)\n", + "df_info[\"n_dim\"] = df_info.var_name.apply(lambda x: len(ds[x].dims))\n", + "df_info[\"attrs\"] = df_info.var_name.apply(lambda x: ds[x].attrs)\n", + "df_info[\"dtype\"] = df_info.var_name.apply(lambda x: ds[x].data.dtype)\n", + "df_info" + ] + }, + { + "cell_type": "markdown", + "id": "e6a36b96", + "metadata": {}, + "source": [ + "### Data cleaning/Preprocessing (skipped)\n", + "Data cleaning and preprocessing is an important stage in the the data analysis pipeline. However it will be out of the scope of this notebook. For more information about data cleaning and preprocessing basics, here are some references. \n", + "* [Xarray Fundamentals](https://earth-env-data-science.github.io/lectures/xarray/xarray.html)\n", + "* [Xarray Tutorial — Pangeo Gallery documentation](http://gallery.pangeo.io/repos/pangeo-data/pangeo-tutorial-gallery/xarray.html)\n", + "* [Pythonic Data Cleaning With pandas and NumPy](https://realpython.com/python-data-cleaning-numpy-pandas/)\n", + "* [Pandas - Cleaning Data](https://www.w3schools.com/python/pandas/pandas_cleaning.asp)" + ] + }, + { + "cell_type": "markdown", + "id": "ae6e1a69", + "metadata": {}, + "source": [ + "### Plotting\n", + "Data visualization (or plotting) is another important data analysis topic and deserves its own discussion. Here in this notebook we will only demonstrate basic tool for simple data visualization tasks.\n", + "\n", + "Here are some reference you might find useful:\n", + "* xarray plotting: https://docs.xarray.dev/en/stable/user-guide/plotting.html\n", + "* Atmospheric Community Toolkit (ACT): https://arm-doe.github.io/ACT/index.html\n", + "\n", + "Note: this notebook is auto-generated using a template. It uses a general idea to select variable(s) to plot and is not customized to each indivdual notebook. Feel free to change the variables in intrrests, especially certain figure is failed to plot." + ] + }, + { + "cell_type": "markdown", + "id": "7e417fb2", + "metadata": {}, + "source": [ + "#### 1-dimenssional basic time series plot\n", + "\n", + "For the following plot we would like to find variables such that\n", + "* it has one and only one dimession\n", + "* \"time\" is its coordinate variable\n", + "* it is not a dimenssion itself,\n", + "* it is not a special variable with substrings within [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "\n", + "Please see the following example in action. (For more details about pandas filtering, please see the following references.)\n", + "* [pandas: multiple conditions while indexing data frame](https://stackoverflow.com/questions/22591174/pandas-multiple-conditions-while-indexing-data-frame-unexpected-behavior)\n", + "* [pandas.DataFrame.apply](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.apply.html)\n", + "* [How to test if a string contains one of the substrings in a list...](https://stackoverflow.com/questions/26577516/how-to-test-if-a-string-contains-one-of-the-substrings-in-a-list-in-pandas" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30edac2d", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==1, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter = df_info[(df_info.n_dim == 1) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4777a659", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_1d = df_filter.var_name.values[0]\n", + " var_1d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb5ae985", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " ds[var_1d].plot()\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "9e41066e", + "metadata": {}, + "source": [ + "#### 2-dimenssional basic plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09bd4adc", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==2, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_2 = df_info[(df_info.n_dim == 2) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2cdea1a9", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_2d = df_filter_2.var_name.values[0]\n", + " var_2d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5f48879", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " print(ds[var_2d].dims)\n", + " # ds[var_2d].plot()\n", + "\n", + " # conventionally, use \"time\" as x-axis\n", + " ds[var_2d].plot(x=\"time\")\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "7554b2a6", + "metadata": {}, + "source": [ + "#### qc-plotting (optional)\n", + "\n", + "Note: act qc plotting has more strict requirement, one of them is the associated qc variable need to have \"flag_masks\" attributes. Which is added by using ds = act.io.armfiles.read_netcdf(files_list), then ds.clean.cleanup()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71c2096f", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " ds_act = act.io.armfiles.read_netcdf(full_path)\n", + " print(type(ds_act))\n", + " ds_act.clean.cleanup()\n", + "\n", + " # or \n", + " # ds.clean.cleanup()\n", + "except Exception as e:\n", + " print(\"ERROR\", e)\n", + " ds_act = ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "844f8505", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter valid variables for ACT qc plotting\n", + "condition = (\"qc_\" + df_info.var_name).apply(lambda x: ds_act[x].attrs.get(\"flag_masks\") is not None \n", + " if x in list(ds.variables) else False)\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_3 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings))) &\n", + " condition\n", + " ]\n", + "df_filter_3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd38cc6", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "try:\n", + " qc_variable = df_filter_3.var_name.values[0]\n", + " print(qc_variable)\n", + "except Exception as e:\n", + " print(e)\n", + " \n", + "try:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + " qc_ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "d76e4d27", + "metadata": {}, + "source": [ + "#### bonus: choose variables to plot from a dropdown menu " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb733804", + "metadata": {}, + "outputs": [], + "source": [ + "# Valid variables filtering\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_4 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82d5c20d", + "metadata": {}, + "outputs": [], + "source": [ + "# example 1: using xarray plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2fc9aaa4", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# fig, ax = plt.subplots(figsize=(10, 4))\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "# fig.clear() # Remove old lines from plot and plot new one\n", + "# if len(ds[var].dims)==2:\n", + "# ds[var].plot(x=\"time\", add_colorbar=False)\n", + "# else:\n", + "# ds[var].plot()\n", + "# plt.grid()\n", + "# plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fabdd802", + "metadata": {}, + "outputs": [], + "source": [ + "# example 2: using act plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "962b4186", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "\n", + "\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "\n", + "# i_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + "# i_display.add_subplots((1,), figsize=(10, 4))\n", + "# ax = i_display.plot(var, subplot_index=(0,), set_title=f\"{var} ({ds_act[var].attrs['long_name']})\",)\n", + "\n", + "# ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "# ax.grid()\n", + "# plt.show()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/2DS-AIR/aaf2dsh.c1.ipynb b/VAPs/quicklook/2DS-AIR/aaf2dsh.c1.ipynb new file mode 100644 index 00000000..e033e77a --- /dev/null +++ b/VAPs/quicklook/2DS-AIR/aaf2dsh.c1.ipynb @@ -0,0 +1,297 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# AAF2DSH.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/2ds-air) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'aaf2dsh'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2018-12-08', 'facility': 'F1', 'site': 'cor', 'start_date': '2018-11-04'}, {'end_date': '2018-02-19', 'facility': 'F1', 'site': 'ena', 'start_date': '2017-06-21'}, {'end_date': '2016-09-22', 'facility': 'F1', 'site': 'sgp', 'start_date': '2016-04-25'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'F1' )\n", + "\n", + "date_start = '2016-09-21'\n", + "date_end = '2016-09-22'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['total_number_concentration', 'number_concentration']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'total_number_concentration'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/2DS-AIR/aaf2dsv.c1.ipynb b/VAPs/quicklook/2DS-AIR/aaf2dsv.c1.ipynb new file mode 100644 index 00000000..376bf7f4 --- /dev/null +++ b/VAPs/quicklook/2DS-AIR/aaf2dsv.c1.ipynb @@ -0,0 +1,297 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# AAF2DSV.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/2ds-air) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'aaf2dsv'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2018-12-08', 'facility': 'F1', 'site': 'cor', 'start_date': '2018-11-04'}, {'end_date': '2018-02-19', 'facility': 'F1', 'site': 'ena', 'start_date': '2017-06-21'}, {'end_date': '2016-09-22', 'facility': 'F1', 'site': 'sgp', 'start_date': '2016-04-25'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'F1' )\n", + "\n", + "date_start = '2016-09-21'\n", + "date_end = '2016-09-22'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['total_number_concentration', 'number_concentration']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'total_number_concentration'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/ACSMCDCE/ACSMCDCE_tutorial.ipynb b/VAPs/quicklook/ACSMCDCE/ACSMCDCE_tutorial.ipynb new file mode 100644 index 00000000..87937544 --- /dev/null +++ b/VAPs/quicklook/ACSMCDCE/ACSMCDCE_tutorial.ipynb @@ -0,0 +1,838 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# ACSMCDCE.C1 Notebook\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/acsmcdce) for more information about this vap." + ] + }, + { + "cell_type": "markdown", + "id": "97097763", + "metadata": {}, + "source": [ + "In this notebook, we demonstrate the workflow to explore ARM vap data (using acsmcdce as an example.) Value-added products (VAPs) are higher-order data products that have been analyzed and processed to ease scientists’ use of ARM data in atmospheric research and global climate models. \n", + "Here is the main content we will cover." + ] + }, + { + "cell_type": "markdown", + "id": "eddec40f", + "metadata": {}, + "source": [ + "# Table of Content\n", + "## Access the data\n", + "* How to retrieve the data\n", + "* Data path and file name conventions\n", + "* Load data\n", + "## Explore the data\n", + "* NetCDF Data structure\n", + "* Xarray essentials\n", + "* Xarray Variable\n", + "## Plot the data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "import random\n", + "\n", + "import glob\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "2372d5be", + "metadata": {}, + "source": [ + "## Access the data" + ] + }, + { + "cell_type": "markdown", + "id": "5b0d9684", + "metadata": {}, + "source": [ + "### How to retrieve the data\n", + "We assume the path \"/data/archive\" is available where you are running this notebook. You can use `os.path.exists(\"/data/archive\")` to verify if the path exists at your machine.\n", + "\n", + "\n", + "### Data path and file name conventions\n", + "There several common terminologies regarding ARM data, for example, data-stream-name, data-level, site, facility, instrument, etc. (For more details, please see [ARM Data File Standards Version 1.2](https://www.google.com/search?q=arm+datastream+facility+cite+definition&rlz=1C1GCEJ_enUS1029US1029&ei=hb41ZICrBPukqtsPvMODIA&ved=0ahUKEwjAgeiS0aL-AhV7kmoFHbzhAAQQ4dUDCBA&uact=5&oq=arm+datastream+facility+cite+definition&gs_lcp=Cgxnd3Mtd2l6LXNlcnAQAzIFCAAQogQyBQgAEKIEMgUIABCiBDIFCAAQogQyBQgAEKIEOgoIABBHENYEELADOgoIIRCgARDDBBAKSgQIQRgAUJ4DWIoOYO0PaAFwAXgAgAGMAYgBuQmSAQMyLjmYAQCgAQHIAQjAAQE&sclient=gws-wiz-serp).) \n", + "\n", + "For example, this notebook is called `acsmcdce.c1`, where `acsmcdce` is the \"datastream name\", and the `{process.ds_class_level}}` is the \"data level\".\n", + "\n", + "This datastream also contains site `epc` and facility `M1`. (Note: individual datastream might have multiple site-facility pairs.)\n", + "In such a case, the data of this data-stream is stored at `/data/archive/epc/epcacsmcdceM1.c1`, which is in the format of `//.`. We can use the following method to assign the data-stream directory `datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )`\n", + "\n", + "The data files under datastream_dir also follows naming conventions. But once reach the datastream_dir level, the most import file naming convention to differentiate the files is \"yyyyMMdd.hhmmss\", which comes handy to filter out files based on datetime. For example, we can use `glob.glob(f'{datastream_dir}/*.200709*.*')` to filter files in 2007 September.\n", + "\n", + "(Note: refer to the https://adc.arm.gov/discovery/#/ and https://adc.arm.gov/solr8/metadata/select API to explore ARM datastream and assoicated available sites and facilities\n", + "\n", + "Please see the following examples in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d7e9eb85", + "metadata": {}, + "outputs": [], + "source": [ + "# Verify if DATA_DIR path exists\n", + "DATA_DIR = \"/data/archive\"\n", + "os.path.exists(DATA_DIR)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "586993fd", + "metadata": {}, + "outputs": [], + "source": [ + "# Speicify datastream_dir following the path conventions and check its existence\n", + "DATASTREAM_NAME = \"acsmcdce\"\n", + "DATA_LEVEL = \"c1\"\n", + "site = \"epc\"\n", + "facility = \"M1\"\n", + "datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )\n", + "print(datastream_dir)\n", + "print(os.path.exists(datastream_dir))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0742f7c1", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: list 5 (random) files under datastream_dir\n", + "files = os.listdir(datastream_dir)\n", + "files[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39b98a36", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: get most recent file\n", + "list_of_files = glob.glob(f\"{datastream_dir}/*\") # * means all if need specific format then *.csv\n", + "latest_file = max(list_of_files, key=os.path.getctime)\n", + "latest_file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "902d514e", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus sort datastream files based on datetime\n", + "files = os.listdir(datastream_dir)\n", + "file_sorted = files.copy()\n", + "file_sorted.sort() \n", + "print(file_sorted[:5])\n", + "\n", + "# to reverse\n", + "file_sorted_reverse = files.copy() \n", + "file_sorted_reverse.sort(reverse=True)\n", + "print(file_sorted_reverse[:5])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec5923b2", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: pattern matching\n", + "# filter the 200709** files under datastream_dir\n", + "files_filter = glob.glob(f'{datastream_dir}/*.200709*.*')\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c4a4aa18", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: use armnotebook_utils.file_filter (TODO) to filter files based on datastream info \n" + ] + }, + { + "cell_type": "markdown", + "id": "cfeb9efc", + "metadata": {}, + "source": [ + "### Load data\n", + "The arm data is stored in [NetCDF](https://en.wikipedia.org/wiki/NetCDF#:~:text=%22NetCDF%20(network%20Common%20Data%20Form,format%20for%20representing%20scientific%20data.) format. We can use xarray's `open_dataset` method to load single file, or `open_mfdataset` to open multiple files. (Note: the latter will still return a single xarray dataset object by combining multiple files.)\n", + "\n", + "Note: open_dataset keeps the file handle open and lazy loads its contents. All parameters are passed directly to open_dataset. It is a preferable method over load_dataset for memory-effiency. (more details, please see [xarray.open_dataset](https://docs.xarray.dev/en/stable/generated/xarray.open_dataset.html))\n", + "\n", + "See the following example in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a440a329", + "metadata": {}, + "outputs": [], + "source": [ + "# open single file\n", + "full_path = latest_file\n", + "print(full_path)\n", + "ds_single = xr.open_dataset(full_path)\n", + "ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0143a3d", + "metadata": {}, + "outputs": [], + "source": [ + "print(ds_single)\n", + "print(type(ds_single))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c0f8939", + "metadata": {}, + "outputs": [], + "source": [ + "# open multiple files\n", + "n_files = 3\n", + "full_paths = [os.path.join(datastream_dir, f_path) for f_path in file_sorted_reverse[:n_files]]\n", + "print(full_paths)\n", + "try: # Note: sometimes multiple files cannot be merged, so we used try except here.\n", + " ds_mutiple = xr.open_mfdataset(full_paths)\n", + " # ds_mutiple\n", + " print(type(ds_mutiple))\n", + " print(ds_mutiple)\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "3fa59943", + "metadata": {}, + "source": [ + "## Explore the data" + ] + }, + { + "cell_type": "markdown", + "id": "571f69b9", + "metadata": {}, + "source": [ + "### NetCDF Data structure \n", + "\n", + "\n", + "(If you are confident with NetCDF basics and xarray essentials, feel free to skip this session.)\n", + "Before we dive into data exploration, there are some eseential concpets we should be familiar with (shown below): \n", + "* Dataset\n", + "* Data array\n", + "* Variable\n", + "* Dimenssion\n", + "* Coordinate\n", + "* Data Type\n", + "* Meta Data (Attributes)\n", + "\n", + "We will not go into details about NetCDF basics and here are some references you might find helpful\n", + "* [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#)\n", + "* [Network Common Data Form (NetCDF)](https://www.unidata.ucar.edu/software/netcdf/) and [A Brief History of (netCDF) Time](https://www.unidata.ucar.edu/software/netcdf/time/recs.html)\n", + "\n", + "\n", + "\n", + "### Xarray essentials\n", + "Earlier we introduced the xarray pacakge and used `open_dataset` and `open_mfdataset` to retrieve NetCDF data file as an xarray Dataset object. Recall that you can review the data in a notebook by using `print(ds)` or `ds`. \n", + "\n", + "Using xarray to retrieve the aforementioned NetCDF basics is straightforwared. In general,\n", + "* Dataset: `ds`\n", + "* Data array: `ds.variables`\n", + "* Variable: `ds.ds.variables`\n", + "* Dimenssion: `ds.dims`\n", + "* Coordinate: `ds.coords`\n", + "* Data Type: `type`\n", + "* Meta Data (Attributes)\n", + "\n", + "Also, here are some references if you are new to xarray\n", + "* [Xarray in 45 minutes](https://tutorial.xarray.dev/overview/xarray-in-45-min.html)\n", + "* [Handling NetCDF Files using XArray for Absolute Beginners](https://towardsdatascience.com/handling-netcdf-files-using-xarray-for-absolute-beginners-111a8ab4463f)\n", + "\n", + "Try the following commands in action\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b3eac323", + "metadata": {}, + "outputs": [], + "source": [ + "ds = ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77ecf85d", + "metadata": {}, + "outputs": [], + "source": [ + "# Dataset \n", + "ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25e7de09", + "metadata": {}, + "outputs": [], + "source": [ + "# Data array, Variable\n", + "# Note: the info can be overwhelming. Do not attempt to grasp everything at the first glance.\n", + "ds.variables" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c41b67e", + "metadata": {}, + "outputs": [], + "source": [ + "# dimenssions\n", + "ds.dims" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "156f1dfc", + "metadata": {}, + "outputs": [], + "source": [ + "# coordinates\n", + "ds.coords" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "277d6064", + "metadata": {}, + "outputs": [], + "source": [ + "# Meta Data (Attributes)\n", + "ds.attrs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d334681f", + "metadata": {}, + "outputs": [], + "source": [ + "# type\n", + "print(type(ds))\n", + "print(type(ds.variables))\n", + "print(type(ds.dims))\n", + "print(type(ds.coords))\n", + "print(type(ds.attrs))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "643399d6", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: retrieve variable names only\n", + "list(ds.variables)" + ] + }, + { + "cell_type": "markdown", + "id": "1cf36878", + "metadata": {}, + "source": [ + "#### Discussion: variable vs. coordinates vs. dimenssions. \n", + "From [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#):\n", + "\n", + "* Dimession: A dimension may be used to represent a real physical dimension, for example, time, latitude, longitude, or height. A dimension might also be used to index other quantities, for example station or model-run-number.\n", + "A netCDF dimension has both a name and a length. A dimension length is an arbitrary positive integer, except that one dimension in a netCDF dataset can have the length UNLIMITED.\n", + "\n", + "* Variables: Variables are used to store the bulk of the data in a netCDF dataset. A variable represents an array of values of the same type. A scalar value is treated as a 0-dimensional array. A variable has a name, a data type, and a shape described by its list of dimensions specified when the variable is created. A variable may also have associated attributes, which may be added, deleted or changed after the variable is created.\n", + "\n", + "* Coordinate (Variables): It is legal for a variable to have the same name as a dimension. Such variables have no special meaning to the netCDF library. However there is a convention that such variables should be treated in a special way by software using this library.\n", + "A variable with the same name as a dimension is called a coordinate variable. It typically defines a physical coordinate corresponding to that dimension.\n", + "\n", + "\n", + "Tips: By definition, coordinates and dimenssions are also variable. For example ds.time is a coordinate but it is also a special variable. But in practice, when we talk about variable, it implies regular/non-coordinate variable. We will use this convention for the remaining notebook. " + ] + }, + { + "cell_type": "markdown", + "id": "d47120f9", + "metadata": {}, + "source": [ + "### Xarray Variable\n", + "\n", + "\n", + "We can use `ds.variables` to access varialbes of a dataset. But many times viewing print-out the whole datasets can be overwhemling. Instead, we would work on the individual variable (also called Xarray data array.) using the `ds[var_name]` syntax, where `var_name` is the name of the variable. See the following examples in action.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5a57e136", + "metadata": {}, + "outputs": [], + "source": [ + "print(type(ds[\"time\"]))\n", + "ds[\"time\"]" + ] + }, + { + "cell_type": "markdown", + "id": "51ec643e", + "metadata": {}, + "source": [ + "#### Variable properties\n", + "For individual varible, the following properties are mostly used with the assocaited xarray method to retrieve them. (assuming var = ds[var_name])\n", + "* name: `var.name`\n", + "* data content: `var.data`\n", + "* attributes: `var.attrs`\n", + "* dimenstions: `var.dims`\n", + "* data type: `var.data.dtype`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a632a525", + "metadata": {}, + "outputs": [], + "source": [ + "var_name = \"time\"\n", + "var = ds[var_name]\n", + "\n", + "print(\"var.name: \\n\", var.name, \"\\n\")\n", + "print(\"var.data: \\n\", var.data, \"\\n\")\n", + "print(\"var.attrs: \\n\", var.attrs, \"\\n\")\n", + "print(\"var.dims: \\n\", var.dims, \"\\n\")\n", + "print(\"var.data.dtype: \\n\", var.data.dtype, \"\\n\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b256b07f", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: indivdual variable info table (collect and display variable info as a tabular format)\n", + "df_info = pd.DataFrame()\n", + "df_info[\"var_name\"] = list(ds.variables)\n", + "df_info[\"dims\"] = df_info.var_name.apply(lambda x: ds[x].dims)\n", + "df_info[\"is_dim\"] = df_info.var_name.apply(lambda x: x in ds.dims)\n", + "df_info[\"n_dim\"] = df_info.var_name.apply(lambda x: len(ds[x].dims))\n", + "df_info[\"attrs\"] = df_info.var_name.apply(lambda x: ds[x].attrs)\n", + "df_info[\"dtype\"] = df_info.var_name.apply(lambda x: ds[x].data.dtype)\n", + "df_info" + ] + }, + { + "cell_type": "markdown", + "id": "e6a36b96", + "metadata": {}, + "source": [ + "### Data cleaning/Preprocessing (skipped)\n", + "Data cleaning and preprocessing is an important stage in the the data analysis pipeline. However it will be out of the scope of this notebook. For more information about data cleaning and preprocessing basics, here are some references. \n", + "* [Xarray Fundamentals](https://earth-env-data-science.github.io/lectures/xarray/xarray.html)\n", + "* [Xarray Tutorial — Pangeo Gallery documentation](http://gallery.pangeo.io/repos/pangeo-data/pangeo-tutorial-gallery/xarray.html)\n", + "* [Pythonic Data Cleaning With pandas and NumPy](https://realpython.com/python-data-cleaning-numpy-pandas/)\n", + "* [Pandas - Cleaning Data](https://www.w3schools.com/python/pandas/pandas_cleaning.asp)" + ] + }, + { + "cell_type": "markdown", + "id": "ae6e1a69", + "metadata": {}, + "source": [ + "### Plotting\n", + "Data visualization (or plotting) is another important data analysis topic and deserves its own discussion. Here in this notebook we will only demonstrate basic tool for simple data visualization tasks.\n", + "\n", + "Here are some reference you might find useful:\n", + "* xarray plotting: https://docs.xarray.dev/en/stable/user-guide/plotting.html\n", + "* Atmospheric Community Toolkit (ACT): https://arm-doe.github.io/ACT/index.html\n", + "\n", + "Note: this notebook is auto-generated using a template. It uses a general idea to select variable(s) to plot and is not customized to each indivdual notebook. Feel free to change the variables in intrrests, especially certain figure is failed to plot." + ] + }, + { + "cell_type": "markdown", + "id": "7e417fb2", + "metadata": {}, + "source": [ + "#### 1-dimenssional basic time series plot\n", + "\n", + "For the following plot we would like to find variables such that\n", + "* it has one and only one dimession\n", + "* \"time\" is its coordinate variable\n", + "* it is not a dimenssion itself,\n", + "* it is not a special variable with substrings within [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "\n", + "Please see the following example in action. (For more details about pandas filtering, please see the following references.)\n", + "* [pandas: multiple conditions while indexing data frame](https://stackoverflow.com/questions/22591174/pandas-multiple-conditions-while-indexing-data-frame-unexpected-behavior)\n", + "* [pandas.DataFrame.apply](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.apply.html)\n", + "* [How to test if a string contains one of the substrings in a list...](https://stackoverflow.com/questions/26577516/how-to-test-if-a-string-contains-one-of-the-substrings-in-a-list-in-pandas" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30edac2d", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==1, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter = df_info[(df_info.n_dim == 1) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4777a659", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_1d = df_filter.var_name.values[0]\n", + " var_1d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb5ae985", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " ds[var_1d].plot()\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "9e41066e", + "metadata": {}, + "source": [ + "#### 2-dimenssional basic plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09bd4adc", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==2, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_2 = df_info[(df_info.n_dim == 2) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2cdea1a9", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_2d = df_filter_2.var_name.values[0]\n", + " var_2d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5f48879", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " print(ds[var_2d].dims)\n", + " # ds[var_2d].plot()\n", + "\n", + " # conventionally, use \"time\" as x-axis\n", + " ds[var_2d].plot(x=\"time\")\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "7554b2a6", + "metadata": {}, + "source": [ + "#### qc-plotting (optional)\n", + "\n", + "Note: act qc plotting has more strict requirement, one of them is the associated qc variable need to have \"flag_masks\" attributes. Which is added by using ds = act.io.armfiles.read_netcdf(files_list), then ds.clean.cleanup()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71c2096f", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " ds_act = act.io.armfiles.read_netcdf(full_path)\n", + " print(type(ds_act))\n", + " ds_act.clean.cleanup()\n", + "\n", + " # or \n", + " # ds.clean.cleanup()\n", + "except Exception as e:\n", + " print(\"ERROR\", e)\n", + " ds_act = ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "844f8505", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter valid variables for ACT qc plotting\n", + "condition = (\"qc_\" + df_info.var_name).apply(lambda x: ds_act[x].attrs.get(\"flag_masks\") is not None \n", + " if x in list(ds.variables) else False)\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_3 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings))) &\n", + " condition\n", + " ]\n", + "df_filter_3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd38cc6", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "try:\n", + " qc_variable = df_filter_3.var_name.values[0]\n", + " print(qc_variable)\n", + "except Exception as e:\n", + " print(e)\n", + " \n", + "try:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + " qc_ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "d76e4d27", + "metadata": {}, + "source": [ + "#### bonus: choose variables to plot from a dropdown menu " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb733804", + "metadata": {}, + "outputs": [], + "source": [ + "# Valid variables filtering\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_4 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82d5c20d", + "metadata": {}, + "outputs": [], + "source": [ + "# example 1: using xarray plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2fc9aaa4", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# fig, ax = plt.subplots(figsize=(10, 4))\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "# fig.clear() # Remove old lines from plot and plot new one\n", + "# if len(ds[var].dims)==2:\n", + "# ds[var].plot(x=\"time\", add_colorbar=False)\n", + "# else:\n", + "# ds[var].plot()\n", + "# plt.grid()\n", + "# plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fabdd802", + "metadata": {}, + "outputs": [], + "source": [ + "# example 2: using act plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "962b4186", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "\n", + "\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "\n", + "# i_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + "# i_display.add_subplots((1,), figsize=(10, 4))\n", + "# ax = i_display.plot(var, subplot_index=(0,), set_title=f\"{var} ({ds_act[var].attrs['long_name']})\",)\n", + "\n", + "# ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "# ax.grid()\n", + "# plt.show()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/ACSMCDCE/acsmcdce.c1.ipynb b/VAPs/quicklook/ACSMCDCE/acsmcdce.c1.ipynb new file mode 100644 index 00000000..5d56f5bf --- /dev/null +++ b/VAPs/quicklook/ACSMCDCE/acsmcdce.c1.ipynb @@ -0,0 +1,339 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# ACSMCDCE.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/acsmcdce) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'acsmcdce'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2023-12-17', 'facility': 'M1', 'site': 'epc', 'start_date': '2023-01-18'}, {'end_date': '2022-09-29', 'facility': 'M1', 'site': 'hou', 'start_date': '2021-09-10'}, {'end_date': '2023-12-10', 'facility': 'E13', 'site': 'sgp', 'start_date': '2019-10-01'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'E13' )\n", + "\n", + "date_start = '2023-12-08'\n", + "date_end = '2023-12-10'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['total_organics', 'ammonium', 'sulfate']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'total_organics'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'total_organics'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/ACSMCDCE/acsmcdce.c2.ipynb b/VAPs/quicklook/ACSMCDCE/acsmcdce.c2.ipynb new file mode 100644 index 00000000..785494db --- /dev/null +++ b/VAPs/quicklook/ACSMCDCE/acsmcdce.c2.ipynb @@ -0,0 +1,339 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# ACSMCDCE.C2 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/acsmcdce) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'acsmcdce'\n", + "DATA_LEVEL = 'c2'\n", + "LOCATIONS = [{'end_date': '2019-05-01', 'facility': 'M1', 'site': 'cor', 'start_date': '2018-09-28'}, {'end_date': '2017-11-01', 'facility': 'M1', 'site': 'asi', 'start_date': '2016-06-02'}, {'end_date': '2023-04-21', 'facility': 'M1', 'site': 'epc', 'start_date': '2023-02-09'}, {'end_date': '2022-10-01', 'facility': 'M1', 'site': 'hou', 'start_date': '2021-09-26'}, {'end_date': '2022-09-29', 'facility': 'S3', 'site': 'hou', 'start_date': '2022-05-28'}, {'end_date': '2023-04-21', 'facility': 'C1', 'site': 'ena', 'start_date': '2014-05-16'}, {'end_date': '2023-04-22', 'facility': 'S2', 'site': 'guc', 'start_date': '2022-04-02'}, {'end_date': '2016-10-03', 'facility': 'C1', 'site': 'sgp', 'start_date': '2010-11-18'}, {'end_date': '2023-04-21', 'facility': 'E13', 'site': 'sgp', 'start_date': '2016-11-29'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2016-10-01'\n", + "date_end = '2016-10-03'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['total_organics_CDCE', 'sulfate_CDCE', 'ammonium_CDCE']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'total_organics_CDCE'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'total_organics_CDCE'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/ACSMCDCE/acsmtofcdce.c1.ipynb b/VAPs/quicklook/ACSMCDCE/acsmtofcdce.c1.ipynb new file mode 100644 index 00000000..17a132ec --- /dev/null +++ b/VAPs/quicklook/ACSMCDCE/acsmtofcdce.c1.ipynb @@ -0,0 +1,339 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# ACSMTOFCDCE.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/acsmcdce) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'acsmtofcdce'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2022-09-28', 'facility': 'S3', 'site': 'hou', 'start_date': '2022-06-01'}, {'end_date': '2023-06-13', 'facility': 'S2', 'site': 'guc', 'start_date': '2022-05-12'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'hou', 'S3' )\n", + "\n", + "date_start = '2022-09-26'\n", + "date_end = '2022-09-28'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['chloride', 'ammonium', 'nitrate']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'chloride'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'chloride'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/AERINF/AERINF_tutorial.ipynb b/VAPs/quicklook/AERINF/AERINF_tutorial.ipynb new file mode 100644 index 00000000..bf583d23 --- /dev/null +++ b/VAPs/quicklook/AERINF/AERINF_tutorial.ipynb @@ -0,0 +1,838 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# AERICH1NF1TURN.C1 Notebook\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/aerinf) for more information about this vap." + ] + }, + { + "cell_type": "markdown", + "id": "97097763", + "metadata": {}, + "source": [ + "In this notebook, we demonstrate the workflow to explore ARM vap data (using aerich1nf1turn as an example.) Value-added products (VAPs) are higher-order data products that have been analyzed and processed to ease scientists’ use of ARM data in atmospheric research and global climate models. \n", + "Here is the main content we will cover." + ] + }, + { + "cell_type": "markdown", + "id": "eddec40f", + "metadata": {}, + "source": [ + "# Table of Content\n", + "## Access the data\n", + "* How to retrieve the data\n", + "* Data path and file name conventions\n", + "* Load data\n", + "## Explore the data\n", + "* NetCDF Data structure\n", + "* Xarray essentials\n", + "* Xarray Variable\n", + "## Plot the data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "import random\n", + "\n", + "import glob\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "2372d5be", + "metadata": {}, + "source": [ + "## Access the data" + ] + }, + { + "cell_type": "markdown", + "id": "5b0d9684", + "metadata": {}, + "source": [ + "### How to retrieve the data\n", + "We assume the path \"/data/archive\" is available where you are running this notebook. You can use `os.path.exists(\"/data/archive\")` to verify if the path exists at your machine.\n", + "\n", + "\n", + "### Data path and file name conventions\n", + "There several common terminologies regarding ARM data, for example, data-stream-name, data-level, site, facility, instrument, etc. (For more details, please see [ARM Data File Standards Version 1.2](https://www.google.com/search?q=arm+datastream+facility+cite+definition&rlz=1C1GCEJ_enUS1029US1029&ei=hb41ZICrBPukqtsPvMODIA&ved=0ahUKEwjAgeiS0aL-AhV7kmoFHbzhAAQQ4dUDCBA&uact=5&oq=arm+datastream+facility+cite+definition&gs_lcp=Cgxnd3Mtd2l6LXNlcnAQAzIFCAAQogQyBQgAEKIEMgUIABCiBDIFCAAQogQyBQgAEKIEOgoIABBHENYEELADOgoIIRCgARDDBBAKSgQIQRgAUJ4DWIoOYO0PaAFwAXgAgAGMAYgBuQmSAQMyLjmYAQCgAQHIAQjAAQE&sclient=gws-wiz-serp).) \n", + "\n", + "For example, this notebook is called `aerich1nf1turn.c1`, where `aerich1nf1turn` is the \"datastream name\", and the `{process.ds_class_level}}` is the \"data level\".\n", + "\n", + "This datastream also contains site `cor` and facility `M1`. (Note: individual datastream might have multiple site-facility pairs.)\n", + "In such a case, the data of this data-stream is stored at `/data/archive/cor/coraerich1nf1turnM1.c1`, which is in the format of `//.`. We can use the following method to assign the data-stream directory `datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )`\n", + "\n", + "The data files under datastream_dir also follows naming conventions. But once reach the datastream_dir level, the most import file naming convention to differentiate the files is \"yyyyMMdd.hhmmss\", which comes handy to filter out files based on datetime. For example, we can use `glob.glob(f'{datastream_dir}/*.200709*.*')` to filter files in 2007 September.\n", + "\n", + "(Note: refer to the https://adc.arm.gov/discovery/#/ and https://adc.arm.gov/solr8/metadata/select API to explore ARM datastream and assoicated available sites and facilities\n", + "\n", + "Please see the following examples in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d7e9eb85", + "metadata": {}, + "outputs": [], + "source": [ + "# Verify if DATA_DIR path exists\n", + "DATA_DIR = \"/data/archive\"\n", + "os.path.exists(DATA_DIR)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "586993fd", + "metadata": {}, + "outputs": [], + "source": [ + "# Speicify datastream_dir following the path conventions and check its existence\n", + "DATASTREAM_NAME = \"aerich1nf1turn\"\n", + "DATA_LEVEL = \"c1\"\n", + "site = \"cor\"\n", + "facility = \"M1\"\n", + "datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )\n", + "print(datastream_dir)\n", + "print(os.path.exists(datastream_dir))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0742f7c1", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: list 5 (random) files under datastream_dir\n", + "files = os.listdir(datastream_dir)\n", + "files[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39b98a36", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: get most recent file\n", + "list_of_files = glob.glob(f\"{datastream_dir}/*\") # * means all if need specific format then *.csv\n", + "latest_file = max(list_of_files, key=os.path.getctime)\n", + "latest_file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "902d514e", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus sort datastream files based on datetime\n", + "files = os.listdir(datastream_dir)\n", + "file_sorted = files.copy()\n", + "file_sorted.sort() \n", + "print(file_sorted[:5])\n", + "\n", + "# to reverse\n", + "file_sorted_reverse = files.copy() \n", + "file_sorted_reverse.sort(reverse=True)\n", + "print(file_sorted_reverse[:5])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec5923b2", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: pattern matching\n", + "# filter the 200709** files under datastream_dir\n", + "files_filter = glob.glob(f'{datastream_dir}/*.200709*.*')\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c4a4aa18", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: use armnotebook_utils.file_filter (TODO) to filter files based on datastream info \n" + ] + }, + { + "cell_type": "markdown", + "id": "cfeb9efc", + "metadata": {}, + "source": [ + "### Load data\n", + "The arm data is stored in [NetCDF](https://en.wikipedia.org/wiki/NetCDF#:~:text=%22NetCDF%20(network%20Common%20Data%20Form,format%20for%20representing%20scientific%20data.) format. We can use xarray's `open_dataset` method to load single file, or `open_mfdataset` to open multiple files. (Note: the latter will still return a single xarray dataset object by combining multiple files.)\n", + "\n", + "Note: open_dataset keeps the file handle open and lazy loads its contents. All parameters are passed directly to open_dataset. It is a preferable method over load_dataset for memory-effiency. (more details, please see [xarray.open_dataset](https://docs.xarray.dev/en/stable/generated/xarray.open_dataset.html))\n", + "\n", + "See the following example in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a440a329", + "metadata": {}, + "outputs": [], + "source": [ + "# open single file\n", + "full_path = latest_file\n", + "print(full_path)\n", + "ds_single = xr.open_dataset(full_path)\n", + "ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0143a3d", + "metadata": {}, + "outputs": [], + "source": [ + "print(ds_single)\n", + "print(type(ds_single))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c0f8939", + "metadata": {}, + "outputs": [], + "source": [ + "# open multiple files\n", + "n_files = 3\n", + "full_paths = [os.path.join(datastream_dir, f_path) for f_path in file_sorted_reverse[:n_files]]\n", + "print(full_paths)\n", + "try: # Note: sometimes multiple files cannot be merged, so we used try except here.\n", + " ds_mutiple = xr.open_mfdataset(full_paths)\n", + " # ds_mutiple\n", + " print(type(ds_mutiple))\n", + " print(ds_mutiple)\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "3fa59943", + "metadata": {}, + "source": [ + "## Explore the data" + ] + }, + { + "cell_type": "markdown", + "id": "571f69b9", + "metadata": {}, + "source": [ + "### NetCDF Data structure \n", + "\n", + "\n", + "(If you are confident with NetCDF basics and xarray essentials, feel free to skip this session.)\n", + "Before we dive into data exploration, there are some eseential concpets we should be familiar with (shown below): \n", + "* Dataset\n", + "* Data array\n", + "* Variable\n", + "* Dimenssion\n", + "* Coordinate\n", + "* Data Type\n", + "* Meta Data (Attributes)\n", + "\n", + "We will not go into details about NetCDF basics and here are some references you might find helpful\n", + "* [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#)\n", + "* [Network Common Data Form (NetCDF)](https://www.unidata.ucar.edu/software/netcdf/) and [A Brief History of (netCDF) Time](https://www.unidata.ucar.edu/software/netcdf/time/recs.html)\n", + "\n", + "\n", + "\n", + "### Xarray essentials\n", + "Earlier we introduced the xarray pacakge and used `open_dataset` and `open_mfdataset` to retrieve NetCDF data file as an xarray Dataset object. Recall that you can review the data in a notebook by using `print(ds)` or `ds`. \n", + "\n", + "Using xarray to retrieve the aforementioned NetCDF basics is straightforwared. In general,\n", + "* Dataset: `ds`\n", + "* Data array: `ds.variables`\n", + "* Variable: `ds.ds.variables`\n", + "* Dimenssion: `ds.dims`\n", + "* Coordinate: `ds.coords`\n", + "* Data Type: `type`\n", + "* Meta Data (Attributes)\n", + "\n", + "Also, here are some references if you are new to xarray\n", + "* [Xarray in 45 minutes](https://tutorial.xarray.dev/overview/xarray-in-45-min.html)\n", + "* [Handling NetCDF Files using XArray for Absolute Beginners](https://towardsdatascience.com/handling-netcdf-files-using-xarray-for-absolute-beginners-111a8ab4463f)\n", + "\n", + "Try the following commands in action\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b3eac323", + "metadata": {}, + "outputs": [], + "source": [ + "ds = ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77ecf85d", + "metadata": {}, + "outputs": [], + "source": [ + "# Dataset \n", + "ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25e7de09", + "metadata": {}, + "outputs": [], + "source": [ + "# Data array, Variable\n", + "# Note: the info can be overwhelming. Do not attempt to grasp everything at the first glance.\n", + "ds.variables" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c41b67e", + "metadata": {}, + "outputs": [], + "source": [ + "# dimenssions\n", + "ds.dims" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "156f1dfc", + "metadata": {}, + "outputs": [], + "source": [ + "# coordinates\n", + "ds.coords" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "277d6064", + "metadata": {}, + "outputs": [], + "source": [ + "# Meta Data (Attributes)\n", + "ds.attrs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d334681f", + "metadata": {}, + "outputs": [], + "source": [ + "# type\n", + "print(type(ds))\n", + "print(type(ds.variables))\n", + "print(type(ds.dims))\n", + "print(type(ds.coords))\n", + "print(type(ds.attrs))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "643399d6", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: retrieve variable names only\n", + "list(ds.variables)" + ] + }, + { + "cell_type": "markdown", + "id": "1cf36878", + "metadata": {}, + "source": [ + "#### Discussion: variable vs. coordinates vs. dimenssions. \n", + "From [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#):\n", + "\n", + "* Dimession: A dimension may be used to represent a real physical dimension, for example, time, latitude, longitude, or height. A dimension might also be used to index other quantities, for example station or model-run-number.\n", + "A netCDF dimension has both a name and a length. A dimension length is an arbitrary positive integer, except that one dimension in a netCDF dataset can have the length UNLIMITED.\n", + "\n", + "* Variables: Variables are used to store the bulk of the data in a netCDF dataset. A variable represents an array of values of the same type. A scalar value is treated as a 0-dimensional array. A variable has a name, a data type, and a shape described by its list of dimensions specified when the variable is created. A variable may also have associated attributes, which may be added, deleted or changed after the variable is created.\n", + "\n", + "* Coordinate (Variables): It is legal for a variable to have the same name as a dimension. Such variables have no special meaning to the netCDF library. However there is a convention that such variables should be treated in a special way by software using this library.\n", + "A variable with the same name as a dimension is called a coordinate variable. It typically defines a physical coordinate corresponding to that dimension.\n", + "\n", + "\n", + "Tips: By definition, coordinates and dimenssions are also variable. For example ds.time is a coordinate but it is also a special variable. But in practice, when we talk about variable, it implies regular/non-coordinate variable. We will use this convention for the remaining notebook. " + ] + }, + { + "cell_type": "markdown", + "id": "d47120f9", + "metadata": {}, + "source": [ + "### Xarray Variable\n", + "\n", + "\n", + "We can use `ds.variables` to access varialbes of a dataset. But many times viewing print-out the whole datasets can be overwhemling. Instead, we would work on the individual variable (also called Xarray data array.) using the `ds[var_name]` syntax, where `var_name` is the name of the variable. See the following examples in action.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5a57e136", + "metadata": {}, + "outputs": [], + "source": [ + "print(type(ds[\"time\"]))\n", + "ds[\"time\"]" + ] + }, + { + "cell_type": "markdown", + "id": "51ec643e", + "metadata": {}, + "source": [ + "#### Variable properties\n", + "For individual varible, the following properties are mostly used with the assocaited xarray method to retrieve them. (assuming var = ds[var_name])\n", + "* name: `var.name`\n", + "* data content: `var.data`\n", + "* attributes: `var.attrs`\n", + "* dimenstions: `var.dims`\n", + "* data type: `var.data.dtype`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a632a525", + "metadata": {}, + "outputs": [], + "source": [ + "var_name = \"time\"\n", + "var = ds[var_name]\n", + "\n", + "print(\"var.name: \\n\", var.name, \"\\n\")\n", + "print(\"var.data: \\n\", var.data, \"\\n\")\n", + "print(\"var.attrs: \\n\", var.attrs, \"\\n\")\n", + "print(\"var.dims: \\n\", var.dims, \"\\n\")\n", + "print(\"var.data.dtype: \\n\", var.data.dtype, \"\\n\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b256b07f", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: indivdual variable info table (collect and display variable info as a tabular format)\n", + "df_info = pd.DataFrame()\n", + "df_info[\"var_name\"] = list(ds.variables)\n", + "df_info[\"dims\"] = df_info.var_name.apply(lambda x: ds[x].dims)\n", + "df_info[\"is_dim\"] = df_info.var_name.apply(lambda x: x in ds.dims)\n", + "df_info[\"n_dim\"] = df_info.var_name.apply(lambda x: len(ds[x].dims))\n", + "df_info[\"attrs\"] = df_info.var_name.apply(lambda x: ds[x].attrs)\n", + "df_info[\"dtype\"] = df_info.var_name.apply(lambda x: ds[x].data.dtype)\n", + "df_info" + ] + }, + { + "cell_type": "markdown", + "id": "e6a36b96", + "metadata": {}, + "source": [ + "### Data cleaning/Preprocessing (skipped)\n", + "Data cleaning and preprocessing is an important stage in the the data analysis pipeline. However it will be out of the scope of this notebook. For more information about data cleaning and preprocessing basics, here are some references. \n", + "* [Xarray Fundamentals](https://earth-env-data-science.github.io/lectures/xarray/xarray.html)\n", + "* [Xarray Tutorial — Pangeo Gallery documentation](http://gallery.pangeo.io/repos/pangeo-data/pangeo-tutorial-gallery/xarray.html)\n", + "* [Pythonic Data Cleaning With pandas and NumPy](https://realpython.com/python-data-cleaning-numpy-pandas/)\n", + "* [Pandas - Cleaning Data](https://www.w3schools.com/python/pandas/pandas_cleaning.asp)" + ] + }, + { + "cell_type": "markdown", + "id": "ae6e1a69", + "metadata": {}, + "source": [ + "### Plotting\n", + "Data visualization (or plotting) is another important data analysis topic and deserves its own discussion. Here in this notebook we will only demonstrate basic tool for simple data visualization tasks.\n", + "\n", + "Here are some reference you might find useful:\n", + "* xarray plotting: https://docs.xarray.dev/en/stable/user-guide/plotting.html\n", + "* Atmospheric Community Toolkit (ACT): https://arm-doe.github.io/ACT/index.html\n", + "\n", + "Note: this notebook is auto-generated using a template. It uses a general idea to select variable(s) to plot and is not customized to each indivdual notebook. Feel free to change the variables in intrrests, especially certain figure is failed to plot." + ] + }, + { + "cell_type": "markdown", + "id": "7e417fb2", + "metadata": {}, + "source": [ + "#### 1-dimenssional basic time series plot\n", + "\n", + "For the following plot we would like to find variables such that\n", + "* it has one and only one dimession\n", + "* \"time\" is its coordinate variable\n", + "* it is not a dimenssion itself,\n", + "* it is not a special variable with substrings within [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "\n", + "Please see the following example in action. (For more details about pandas filtering, please see the following references.)\n", + "* [pandas: multiple conditions while indexing data frame](https://stackoverflow.com/questions/22591174/pandas-multiple-conditions-while-indexing-data-frame-unexpected-behavior)\n", + "* [pandas.DataFrame.apply](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.apply.html)\n", + "* [How to test if a string contains one of the substrings in a list...](https://stackoverflow.com/questions/26577516/how-to-test-if-a-string-contains-one-of-the-substrings-in-a-list-in-pandas" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30edac2d", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==1, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter = df_info[(df_info.n_dim == 1) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4777a659", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_1d = df_filter.var_name.values[0]\n", + " var_1d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb5ae985", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " ds[var_1d].plot()\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "9e41066e", + "metadata": {}, + "source": [ + "#### 2-dimenssional basic plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09bd4adc", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==2, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_2 = df_info[(df_info.n_dim == 2) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2cdea1a9", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_2d = df_filter_2.var_name.values[0]\n", + " var_2d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5f48879", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " print(ds[var_2d].dims)\n", + " # ds[var_2d].plot()\n", + "\n", + " # conventionally, use \"time\" as x-axis\n", + " ds[var_2d].plot(x=\"time\")\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "7554b2a6", + "metadata": {}, + "source": [ + "#### qc-plotting (optional)\n", + "\n", + "Note: act qc plotting has more strict requirement, one of them is the associated qc variable need to have \"flag_masks\" attributes. Which is added by using ds = act.io.armfiles.read_netcdf(files_list), then ds.clean.cleanup()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71c2096f", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " ds_act = act.io.armfiles.read_netcdf(full_path)\n", + " print(type(ds_act))\n", + " ds_act.clean.cleanup()\n", + "\n", + " # or \n", + " # ds.clean.cleanup()\n", + "except Exception as e:\n", + " print(\"ERROR\", e)\n", + " ds_act = ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "844f8505", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter valid variables for ACT qc plotting\n", + "condition = (\"qc_\" + df_info.var_name).apply(lambda x: ds_act[x].attrs.get(\"flag_masks\") is not None \n", + " if x in list(ds.variables) else False)\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_3 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings))) &\n", + " condition\n", + " ]\n", + "df_filter_3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd38cc6", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "try:\n", + " qc_variable = df_filter_3.var_name.values[0]\n", + " print(qc_variable)\n", + "except Exception as e:\n", + " print(e)\n", + " \n", + "try:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + " qc_ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "d76e4d27", + "metadata": {}, + "source": [ + "#### bonus: choose variables to plot from a dropdown menu " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb733804", + "metadata": {}, + "outputs": [], + "source": [ + "# Valid variables filtering\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_4 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82d5c20d", + "metadata": {}, + "outputs": [], + "source": [ + "# example 1: using xarray plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2fc9aaa4", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# fig, ax = plt.subplots(figsize=(10, 4))\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "# fig.clear() # Remove old lines from plot and plot new one\n", + "# if len(ds[var].dims)==2:\n", + "# ds[var].plot(x=\"time\", add_colorbar=False)\n", + "# else:\n", + "# ds[var].plot()\n", + "# plt.grid()\n", + "# plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fabdd802", + "metadata": {}, + "outputs": [], + "source": [ + "# example 2: using act plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "962b4186", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "\n", + "\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "\n", + "# i_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + "# i_display.add_subplots((1,), figsize=(10, 4))\n", + "# ax = i_display.plot(var, subplot_index=(0,), set_title=f\"{var} ({ds_act[var].attrs['long_name']})\",)\n", + "\n", + "# ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "# ax.grid()\n", + "# plt.show()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/AERINF/aerich1nf1turn.c1.ipynb b/VAPs/quicklook/AERINF/aerich1nf1turn.c1.ipynb new file mode 100644 index 00000000..22cb378b --- /dev/null +++ b/VAPs/quicklook/AERINF/aerich1nf1turn.c1.ipynb @@ -0,0 +1,297 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# AERICH1NF1TURN.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/aerinf) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'aerich1nf1turn'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2019-04-30', 'facility': 'M1', 'site': 'cor', 'start_date': '2018-09-27'}, {'end_date': '2016-12-20', 'facility': 'M1', 'site': 'awr', 'start_date': '2015-12-01'}, {'end_date': '2020-05-20', 'facility': 'M1', 'site': 'anx', 'start_date': '2019-12-01'}, {'end_date': '2008-12-27', 'facility': 'M1', 'site': 'hfe', 'start_date': '2008-05-06'}, {'end_date': '2023-12-09', 'facility': 'M1', 'site': 'epc', 'start_date': '2023-01-15'}, {'end_date': '2007-12-31', 'facility': 'M1', 'site': 'fkb', 'start_date': '2007-03-28'}, {'end_date': '2022-10-01', 'facility': 'M1', 'site': 'hou', 'start_date': '2021-08-09'}, {'end_date': '2023-12-10', 'facility': 'C1', 'site': 'ena', 'start_date': '2016-07-21'}, {'end_date': '2015-10-10', 'facility': 'M1', 'site': 'mao', 'start_date': '2014-01-01'}, {'end_date': '2023-06-10', 'facility': 'M1', 'site': 'guc', 'start_date': '2021-09-01'}, {'end_date': '2011-01-05', 'facility': 'M1', 'site': 'grw', 'start_date': '2009-04-12'}, {'end_date': '2023-12-10', 'facility': 'C1', 'site': 'sgp', 'start_date': '2006-02-20'}, {'end_date': '2022-09-20', 'facility': 'E32', 'site': 'sgp', 'start_date': '2016-04-11'}, {'end_date': '2023-10-20', 'facility': 'E37', 'site': 'sgp', 'start_date': '2016-04-11'}, {'end_date': '2023-10-20', 'facility': 'E39', 'site': 'sgp', 'start_date': '2016-04-11'}, {'end_date': '2005-09-13', 'facility': 'M1', 'site': 'pye', 'start_date': '2005-03-23'}, {'end_date': '2020-09-10', 'facility': 'M1', 'site': 'mos', 'start_date': '2019-11-11'}, {'end_date': '2007-01-05', 'facility': 'M1', 'site': 'nim', 'start_date': '2006-01-07'}, {'end_date': '2012-03-24', 'facility': 'M1', 'site': 'pgh', 'start_date': '2011-07-17'}, {'end_date': '2013-04-22', 'facility': 'M1', 'site': 'pvc', 'start_date': '2012-06-22'}, {'end_date': '2021-01-20', 'facility': 'M1', 'site': 'oli', 'start_date': '2014-09-01'}, {'end_date': '2023-12-10', 'facility': 'C1', 'site': 'nsa', 'start_date': '2006-02-20'}, {'end_date': '2014-07-06', 'facility': 'C1', 'site': 'twp', 'start_date': '2011-06-04'}, {'end_date': '2009-02-10', 'facility': 'C2', 'site': 'twp', 'start_date': '2006-02-01'}, {'end_date': '2014-12-31', 'facility': 'C3', 'site': 'twp', 'start_date': '2006-02-01'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2023-12-08'\n", + "date_end = '2023-12-10'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['mean_rad']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'mean_rad'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/AERINF/aerich2nf1turn.c1.ipynb b/VAPs/quicklook/AERINF/aerich2nf1turn.c1.ipynb new file mode 100644 index 00000000..2cf15c4d --- /dev/null +++ b/VAPs/quicklook/AERINF/aerich2nf1turn.c1.ipynb @@ -0,0 +1,297 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# AERICH2NF1TURN.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/aerinf) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'aerich2nf1turn'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2019-04-30', 'facility': 'M1', 'site': 'cor', 'start_date': '2018-09-27'}, {'end_date': '2016-12-20', 'facility': 'M1', 'site': 'awr', 'start_date': '2015-12-01'}, {'end_date': '2020-05-20', 'facility': 'M1', 'site': 'anx', 'start_date': '2019-12-01'}, {'end_date': '2008-12-27', 'facility': 'M1', 'site': 'hfe', 'start_date': '2008-05-06'}, {'end_date': '2023-12-09', 'facility': 'M1', 'site': 'epc', 'start_date': '2023-01-15'}, {'end_date': '2007-12-31', 'facility': 'M1', 'site': 'fkb', 'start_date': '2007-03-28'}, {'end_date': '2022-10-01', 'facility': 'M1', 'site': 'hou', 'start_date': '2021-08-09'}, {'end_date': '2023-12-10', 'facility': 'C1', 'site': 'ena', 'start_date': '2016-07-21'}, {'end_date': '2015-10-10', 'facility': 'M1', 'site': 'mao', 'start_date': '2014-01-01'}, {'end_date': '2023-06-10', 'facility': 'M1', 'site': 'guc', 'start_date': '2021-09-01'}, {'end_date': '2011-01-05', 'facility': 'M1', 'site': 'grw', 'start_date': '2009-04-12'}, {'end_date': '2023-12-10', 'facility': 'C1', 'site': 'sgp', 'start_date': '2006-02-20'}, {'end_date': '2022-09-20', 'facility': 'E32', 'site': 'sgp', 'start_date': '2016-04-11'}, {'end_date': '2023-10-20', 'facility': 'E37', 'site': 'sgp', 'start_date': '2016-04-11'}, {'end_date': '2023-10-20', 'facility': 'E39', 'site': 'sgp', 'start_date': '2016-04-11'}, {'end_date': '2005-09-13', 'facility': 'M1', 'site': 'pye', 'start_date': '2005-03-23'}, {'end_date': '2020-09-10', 'facility': 'M1', 'site': 'mos', 'start_date': '2019-11-11'}, {'end_date': '2007-01-05', 'facility': 'M1', 'site': 'nim', 'start_date': '2006-01-07'}, {'end_date': '2012-03-24', 'facility': 'M1', 'site': 'pgh', 'start_date': '2011-07-11'}, {'end_date': '2013-04-22', 'facility': 'M1', 'site': 'pvc', 'start_date': '2012-06-22'}, {'end_date': '2021-01-20', 'facility': 'M1', 'site': 'oli', 'start_date': '2014-09-01'}, {'end_date': '2023-12-10', 'facility': 'C1', 'site': 'nsa', 'start_date': '2006-02-20'}, {'end_date': '2014-07-06', 'facility': 'C1', 'site': 'twp', 'start_date': '2011-06-04'}, {'end_date': '2009-02-10', 'facility': 'C2', 'site': 'twp', 'start_date': '2006-02-01'}, {'end_date': '2014-12-31', 'facility': 'C3', 'site': 'twp', 'start_date': '2006-02-01'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2023-12-08'\n", + "date_end = '2023-12-10'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['mean_rad']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'mean_rad'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/AERIOE/AERIOE_tutorial.ipynb b/VAPs/quicklook/AERIOE/AERIOE_tutorial.ipynb new file mode 100644 index 00000000..89687394 --- /dev/null +++ b/VAPs/quicklook/AERIOE/AERIOE_tutorial.ipynb @@ -0,0 +1,838 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# AERIOE1TURN.C1 Notebook\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/aerioe) for more information about this vap." + ] + }, + { + "cell_type": "markdown", + "id": "97097763", + "metadata": {}, + "source": [ + "In this notebook, we demonstrate the workflow to explore ARM vap data (using aerioe1turn as an example.) Value-added products (VAPs) are higher-order data products that have been analyzed and processed to ease scientists’ use of ARM data in atmospheric research and global climate models. \n", + "Here is the main content we will cover." + ] + }, + { + "cell_type": "markdown", + "id": "eddec40f", + "metadata": {}, + "source": [ + "# Table of Content\n", + "## Access the data\n", + "* How to retrieve the data\n", + "* Data path and file name conventions\n", + "* Load data\n", + "## Explore the data\n", + "* NetCDF Data structure\n", + "* Xarray essentials\n", + "* Xarray Variable\n", + "## Plot the data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "import random\n", + "\n", + "import glob\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "2372d5be", + "metadata": {}, + "source": [ + "## Access the data" + ] + }, + { + "cell_type": "markdown", + "id": "5b0d9684", + "metadata": {}, + "source": [ + "### How to retrieve the data\n", + "We assume the path \"/data/archive\" is available where you are running this notebook. You can use `os.path.exists(\"/data/archive\")` to verify if the path exists at your machine.\n", + "\n", + "\n", + "### Data path and file name conventions\n", + "There several common terminologies regarding ARM data, for example, data-stream-name, data-level, site, facility, instrument, etc. (For more details, please see [ARM Data File Standards Version 1.2](https://www.google.com/search?q=arm+datastream+facility+cite+definition&rlz=1C1GCEJ_enUS1029US1029&ei=hb41ZICrBPukqtsPvMODIA&ved=0ahUKEwjAgeiS0aL-AhV7kmoFHbzhAAQQ4dUDCBA&uact=5&oq=arm+datastream+facility+cite+definition&gs_lcp=Cgxnd3Mtd2l6LXNlcnAQAzIFCAAQogQyBQgAEKIEMgUIABCiBDIFCAAQogQyBQgAEKIEOgoIABBHENYEELADOgoIIRCgARDDBBAKSgQIQRgAUJ4DWIoOYO0PaAFwAXgAgAGMAYgBuQmSAQMyLjmYAQCgAQHIAQjAAQE&sclient=gws-wiz-serp).) \n", + "\n", + "For example, this notebook is called `aerioe1turn.c1`, where `aerioe1turn` is the \"datastream name\", and the `{process.ds_class_level}}` is the \"data level\".\n", + "\n", + "This datastream also contains site `sgp` and facility `C1`. (Note: individual datastream might have multiple site-facility pairs.)\n", + "In such a case, the data of this data-stream is stored at `/data/archive/sgp/sgpaerioe1turnC1.c1`, which is in the format of `//.`. We can use the following method to assign the data-stream directory `datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )`\n", + "\n", + "The data files under datastream_dir also follows naming conventions. But once reach the datastream_dir level, the most import file naming convention to differentiate the files is \"yyyyMMdd.hhmmss\", which comes handy to filter out files based on datetime. For example, we can use `glob.glob(f'{datastream_dir}/*.200709*.*')` to filter files in 2007 September.\n", + "\n", + "(Note: refer to the https://adc.arm.gov/discovery/#/ and https://adc.arm.gov/solr8/metadata/select API to explore ARM datastream and assoicated available sites and facilities\n", + "\n", + "Please see the following examples in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d7e9eb85", + "metadata": {}, + "outputs": [], + "source": [ + "# Verify if DATA_DIR path exists\n", + "DATA_DIR = \"/data/archive\"\n", + "os.path.exists(DATA_DIR)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "586993fd", + "metadata": {}, + "outputs": [], + "source": [ + "# Speicify datastream_dir following the path conventions and check its existence\n", + "DATASTREAM_NAME = \"aerioe1turn\"\n", + "DATA_LEVEL = \"c1\"\n", + "site = \"sgp\"\n", + "facility = \"C1\"\n", + "datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )\n", + "print(datastream_dir)\n", + "print(os.path.exists(datastream_dir))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0742f7c1", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: list 5 (random) files under datastream_dir\n", + "files = os.listdir(datastream_dir)\n", + "files[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39b98a36", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: get most recent file\n", + "list_of_files = glob.glob(f\"{datastream_dir}/*\") # * means all if need specific format then *.csv\n", + "latest_file = max(list_of_files, key=os.path.getctime)\n", + "latest_file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "902d514e", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus sort datastream files based on datetime\n", + "files = os.listdir(datastream_dir)\n", + "file_sorted = files.copy()\n", + "file_sorted.sort() \n", + "print(file_sorted[:5])\n", + "\n", + "# to reverse\n", + "file_sorted_reverse = files.copy() \n", + "file_sorted_reverse.sort(reverse=True)\n", + "print(file_sorted_reverse[:5])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec5923b2", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: pattern matching\n", + "# filter the 200709** files under datastream_dir\n", + "files_filter = glob.glob(f'{datastream_dir}/*.200709*.*')\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c4a4aa18", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: use armnotebook_utils.file_filter (TODO) to filter files based on datastream info \n" + ] + }, + { + "cell_type": "markdown", + "id": "cfeb9efc", + "metadata": {}, + "source": [ + "### Load data\n", + "The arm data is stored in [NetCDF](https://en.wikipedia.org/wiki/NetCDF#:~:text=%22NetCDF%20(network%20Common%20Data%20Form,format%20for%20representing%20scientific%20data.) format. We can use xarray's `open_dataset` method to load single file, or `open_mfdataset` to open multiple files. (Note: the latter will still return a single xarray dataset object by combining multiple files.)\n", + "\n", + "Note: open_dataset keeps the file handle open and lazy loads its contents. All parameters are passed directly to open_dataset. It is a preferable method over load_dataset for memory-effiency. (more details, please see [xarray.open_dataset](https://docs.xarray.dev/en/stable/generated/xarray.open_dataset.html))\n", + "\n", + "See the following example in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a440a329", + "metadata": {}, + "outputs": [], + "source": [ + "# open single file\n", + "full_path = latest_file\n", + "print(full_path)\n", + "ds_single = xr.open_dataset(full_path)\n", + "ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0143a3d", + "metadata": {}, + "outputs": [], + "source": [ + "print(ds_single)\n", + "print(type(ds_single))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c0f8939", + "metadata": {}, + "outputs": [], + "source": [ + "# open multiple files\n", + "n_files = 3\n", + "full_paths = [os.path.join(datastream_dir, f_path) for f_path in file_sorted_reverse[:n_files]]\n", + "print(full_paths)\n", + "try: # Note: sometimes multiple files cannot be merged, so we used try except here.\n", + " ds_mutiple = xr.open_mfdataset(full_paths)\n", + " # ds_mutiple\n", + " print(type(ds_mutiple))\n", + " print(ds_mutiple)\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "3fa59943", + "metadata": {}, + "source": [ + "## Explore the data" + ] + }, + { + "cell_type": "markdown", + "id": "571f69b9", + "metadata": {}, + "source": [ + "### NetCDF Data structure \n", + "\n", + "\n", + "(If you are confident with NetCDF basics and xarray essentials, feel free to skip this session.)\n", + "Before we dive into data exploration, there are some eseential concpets we should be familiar with (shown below): \n", + "* Dataset\n", + "* Data array\n", + "* Variable\n", + "* Dimenssion\n", + "* Coordinate\n", + "* Data Type\n", + "* Meta Data (Attributes)\n", + "\n", + "We will not go into details about NetCDF basics and here are some references you might find helpful\n", + "* [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#)\n", + "* [Network Common Data Form (NetCDF)](https://www.unidata.ucar.edu/software/netcdf/) and [A Brief History of (netCDF) Time](https://www.unidata.ucar.edu/software/netcdf/time/recs.html)\n", + "\n", + "\n", + "\n", + "### Xarray essentials\n", + "Earlier we introduced the xarray pacakge and used `open_dataset` and `open_mfdataset` to retrieve NetCDF data file as an xarray Dataset object. Recall that you can review the data in a notebook by using `print(ds)` or `ds`. \n", + "\n", + "Using xarray to retrieve the aforementioned NetCDF basics is straightforwared. In general,\n", + "* Dataset: `ds`\n", + "* Data array: `ds.variables`\n", + "* Variable: `ds.ds.variables`\n", + "* Dimenssion: `ds.dims`\n", + "* Coordinate: `ds.coords`\n", + "* Data Type: `type`\n", + "* Meta Data (Attributes)\n", + "\n", + "Also, here are some references if you are new to xarray\n", + "* [Xarray in 45 minutes](https://tutorial.xarray.dev/overview/xarray-in-45-min.html)\n", + "* [Handling NetCDF Files using XArray for Absolute Beginners](https://towardsdatascience.com/handling-netcdf-files-using-xarray-for-absolute-beginners-111a8ab4463f)\n", + "\n", + "Try the following commands in action\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b3eac323", + "metadata": {}, + "outputs": [], + "source": [ + "ds = ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77ecf85d", + "metadata": {}, + "outputs": [], + "source": [ + "# Dataset \n", + "ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25e7de09", + "metadata": {}, + "outputs": [], + "source": [ + "# Data array, Variable\n", + "# Note: the info can be overwhelming. Do not attempt to grasp everything at the first glance.\n", + "ds.variables" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c41b67e", + "metadata": {}, + "outputs": [], + "source": [ + "# dimenssions\n", + "ds.dims" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "156f1dfc", + "metadata": {}, + "outputs": [], + "source": [ + "# coordinates\n", + "ds.coords" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "277d6064", + "metadata": {}, + "outputs": [], + "source": [ + "# Meta Data (Attributes)\n", + "ds.attrs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d334681f", + "metadata": {}, + "outputs": [], + "source": [ + "# type\n", + "print(type(ds))\n", + "print(type(ds.variables))\n", + "print(type(ds.dims))\n", + "print(type(ds.coords))\n", + "print(type(ds.attrs))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "643399d6", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: retrieve variable names only\n", + "list(ds.variables)" + ] + }, + { + "cell_type": "markdown", + "id": "1cf36878", + "metadata": {}, + "source": [ + "#### Discussion: variable vs. coordinates vs. dimenssions. \n", + "From [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#):\n", + "\n", + "* Dimession: A dimension may be used to represent a real physical dimension, for example, time, latitude, longitude, or height. A dimension might also be used to index other quantities, for example station or model-run-number.\n", + "A netCDF dimension has both a name and a length. A dimension length is an arbitrary positive integer, except that one dimension in a netCDF dataset can have the length UNLIMITED.\n", + "\n", + "* Variables: Variables are used to store the bulk of the data in a netCDF dataset. A variable represents an array of values of the same type. A scalar value is treated as a 0-dimensional array. A variable has a name, a data type, and a shape described by its list of dimensions specified when the variable is created. A variable may also have associated attributes, which may be added, deleted or changed after the variable is created.\n", + "\n", + "* Coordinate (Variables): It is legal for a variable to have the same name as a dimension. Such variables have no special meaning to the netCDF library. However there is a convention that such variables should be treated in a special way by software using this library.\n", + "A variable with the same name as a dimension is called a coordinate variable. It typically defines a physical coordinate corresponding to that dimension.\n", + "\n", + "\n", + "Tips: By definition, coordinates and dimenssions are also variable. For example ds.time is a coordinate but it is also a special variable. But in practice, when we talk about variable, it implies regular/non-coordinate variable. We will use this convention for the remaining notebook. " + ] + }, + { + "cell_type": "markdown", + "id": "d47120f9", + "metadata": {}, + "source": [ + "### Xarray Variable\n", + "\n", + "\n", + "We can use `ds.variables` to access varialbes of a dataset. But many times viewing print-out the whole datasets can be overwhemling. Instead, we would work on the individual variable (also called Xarray data array.) using the `ds[var_name]` syntax, where `var_name` is the name of the variable. See the following examples in action.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5a57e136", + "metadata": {}, + "outputs": [], + "source": [ + "print(type(ds[\"time\"]))\n", + "ds[\"time\"]" + ] + }, + { + "cell_type": "markdown", + "id": "51ec643e", + "metadata": {}, + "source": [ + "#### Variable properties\n", + "For individual varible, the following properties are mostly used with the assocaited xarray method to retrieve them. (assuming var = ds[var_name])\n", + "* name: `var.name`\n", + "* data content: `var.data`\n", + "* attributes: `var.attrs`\n", + "* dimenstions: `var.dims`\n", + "* data type: `var.data.dtype`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a632a525", + "metadata": {}, + "outputs": [], + "source": [ + "var_name = \"time\"\n", + "var = ds[var_name]\n", + "\n", + "print(\"var.name: \\n\", var.name, \"\\n\")\n", + "print(\"var.data: \\n\", var.data, \"\\n\")\n", + "print(\"var.attrs: \\n\", var.attrs, \"\\n\")\n", + "print(\"var.dims: \\n\", var.dims, \"\\n\")\n", + "print(\"var.data.dtype: \\n\", var.data.dtype, \"\\n\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b256b07f", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: indivdual variable info table (collect and display variable info as a tabular format)\n", + "df_info = pd.DataFrame()\n", + "df_info[\"var_name\"] = list(ds.variables)\n", + "df_info[\"dims\"] = df_info.var_name.apply(lambda x: ds[x].dims)\n", + "df_info[\"is_dim\"] = df_info.var_name.apply(lambda x: x in ds.dims)\n", + "df_info[\"n_dim\"] = df_info.var_name.apply(lambda x: len(ds[x].dims))\n", + "df_info[\"attrs\"] = df_info.var_name.apply(lambda x: ds[x].attrs)\n", + "df_info[\"dtype\"] = df_info.var_name.apply(lambda x: ds[x].data.dtype)\n", + "df_info" + ] + }, + { + "cell_type": "markdown", + "id": "e6a36b96", + "metadata": {}, + "source": [ + "### Data cleaning/Preprocessing (skipped)\n", + "Data cleaning and preprocessing is an important stage in the the data analysis pipeline. However it will be out of the scope of this notebook. For more information about data cleaning and preprocessing basics, here are some references. \n", + "* [Xarray Fundamentals](https://earth-env-data-science.github.io/lectures/xarray/xarray.html)\n", + "* [Xarray Tutorial — Pangeo Gallery documentation](http://gallery.pangeo.io/repos/pangeo-data/pangeo-tutorial-gallery/xarray.html)\n", + "* [Pythonic Data Cleaning With pandas and NumPy](https://realpython.com/python-data-cleaning-numpy-pandas/)\n", + "* [Pandas - Cleaning Data](https://www.w3schools.com/python/pandas/pandas_cleaning.asp)" + ] + }, + { + "cell_type": "markdown", + "id": "ae6e1a69", + "metadata": {}, + "source": [ + "### Plotting\n", + "Data visualization (or plotting) is another important data analysis topic and deserves its own discussion. Here in this notebook we will only demonstrate basic tool for simple data visualization tasks.\n", + "\n", + "Here are some reference you might find useful:\n", + "* xarray plotting: https://docs.xarray.dev/en/stable/user-guide/plotting.html\n", + "* Atmospheric Community Toolkit (ACT): https://arm-doe.github.io/ACT/index.html\n", + "\n", + "Note: this notebook is auto-generated using a template. It uses a general idea to select variable(s) to plot and is not customized to each indivdual notebook. Feel free to change the variables in intrrests, especially certain figure is failed to plot." + ] + }, + { + "cell_type": "markdown", + "id": "7e417fb2", + "metadata": {}, + "source": [ + "#### 1-dimenssional basic time series plot\n", + "\n", + "For the following plot we would like to find variables such that\n", + "* it has one and only one dimession\n", + "* \"time\" is its coordinate variable\n", + "* it is not a dimenssion itself,\n", + "* it is not a special variable with substrings within [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "\n", + "Please see the following example in action. (For more details about pandas filtering, please see the following references.)\n", + "* [pandas: multiple conditions while indexing data frame](https://stackoverflow.com/questions/22591174/pandas-multiple-conditions-while-indexing-data-frame-unexpected-behavior)\n", + "* [pandas.DataFrame.apply](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.apply.html)\n", + "* [How to test if a string contains one of the substrings in a list...](https://stackoverflow.com/questions/26577516/how-to-test-if-a-string-contains-one-of-the-substrings-in-a-list-in-pandas" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30edac2d", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==1, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter = df_info[(df_info.n_dim == 1) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4777a659", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_1d = df_filter.var_name.values[0]\n", + " var_1d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb5ae985", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " ds[var_1d].plot()\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "9e41066e", + "metadata": {}, + "source": [ + "#### 2-dimenssional basic plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09bd4adc", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==2, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_2 = df_info[(df_info.n_dim == 2) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2cdea1a9", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_2d = df_filter_2.var_name.values[0]\n", + " var_2d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5f48879", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " print(ds[var_2d].dims)\n", + " # ds[var_2d].plot()\n", + "\n", + " # conventionally, use \"time\" as x-axis\n", + " ds[var_2d].plot(x=\"time\")\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "7554b2a6", + "metadata": {}, + "source": [ + "#### qc-plotting (optional)\n", + "\n", + "Note: act qc plotting has more strict requirement, one of them is the associated qc variable need to have \"flag_masks\" attributes. Which is added by using ds = act.io.armfiles.read_netcdf(files_list), then ds.clean.cleanup()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71c2096f", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " ds_act = act.io.armfiles.read_netcdf(full_path)\n", + " print(type(ds_act))\n", + " ds_act.clean.cleanup()\n", + "\n", + " # or \n", + " # ds.clean.cleanup()\n", + "except Exception as e:\n", + " print(\"ERROR\", e)\n", + " ds_act = ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "844f8505", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter valid variables for ACT qc plotting\n", + "condition = (\"qc_\" + df_info.var_name).apply(lambda x: ds_act[x].attrs.get(\"flag_masks\") is not None \n", + " if x in list(ds.variables) else False)\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_3 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings))) &\n", + " condition\n", + " ]\n", + "df_filter_3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd38cc6", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "try:\n", + " qc_variable = df_filter_3.var_name.values[0]\n", + " print(qc_variable)\n", + "except Exception as e:\n", + " print(e)\n", + " \n", + "try:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + " qc_ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "d76e4d27", + "metadata": {}, + "source": [ + "#### bonus: choose variables to plot from a dropdown menu " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb733804", + "metadata": {}, + "outputs": [], + "source": [ + "# Valid variables filtering\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_4 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82d5c20d", + "metadata": {}, + "outputs": [], + "source": [ + "# example 1: using xarray plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2fc9aaa4", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# fig, ax = plt.subplots(figsize=(10, 4))\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "# fig.clear() # Remove old lines from plot and plot new one\n", + "# if len(ds[var].dims)==2:\n", + "# ds[var].plot(x=\"time\", add_colorbar=False)\n", + "# else:\n", + "# ds[var].plot()\n", + "# plt.grid()\n", + "# plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fabdd802", + "metadata": {}, + "outputs": [], + "source": [ + "# example 2: using act plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "962b4186", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "\n", + "\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "\n", + "# i_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + "# i_display.add_subplots((1,), figsize=(10, 4))\n", + "# ax = i_display.plot(var, subplot_index=(0,), set_title=f\"{var} ({ds_act[var].attrs['long_name']})\",)\n", + "\n", + "# ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "# ax.grid()\n", + "# plt.show()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/AERIOE/aerioe1turn.c1.ipynb b/VAPs/quicklook/AERIOE/aerioe1turn.c1.ipynb new file mode 100644 index 00000000..45c47071 --- /dev/null +++ b/VAPs/quicklook/AERIOE/aerioe1turn.c1.ipynb @@ -0,0 +1,297 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# AERIOE1TURN.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/aerioe) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'aerioe1turn'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2023-06-13', 'facility': 'C1', 'site': 'sgp', 'start_date': '2016-01-01'}, {'end_date': '2021-06-01', 'facility': 'E32', 'site': 'sgp', 'start_date': '2016-08-10'}, {'end_date': '2021-05-18', 'facility': 'E37', 'site': 'sgp', 'start_date': '2016-08-10'}, {'end_date': '2022-07-09', 'facility': 'E39', 'site': 'sgp', 'start_date': '2016-08-12'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2023-06-11'\n", + "date_end = '2023-06-13'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['temperature', 'waterVapor', 'lwp']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'temperature'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/AERIPROF/AERIPROF_tutorial.ipynb b/VAPs/quicklook/AERIPROF/AERIPROF_tutorial.ipynb new file mode 100644 index 00000000..5ffe92ed --- /dev/null +++ b/VAPs/quicklook/AERIPROF/AERIPROF_tutorial.ipynb @@ -0,0 +1,838 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# AERI01PROF3FELTZ.C1 Notebook\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/aeriprof) for more information about this vap." + ] + }, + { + "cell_type": "markdown", + "id": "97097763", + "metadata": {}, + "source": [ + "In this notebook, we demonstrate the workflow to explore ARM vap data (using aeri01prof3feltz as an example.) Value-added products (VAPs) are higher-order data products that have been analyzed and processed to ease scientists’ use of ARM data in atmospheric research and global climate models. \n", + "Here is the main content we will cover." + ] + }, + { + "cell_type": "markdown", + "id": "eddec40f", + "metadata": {}, + "source": [ + "# Table of Content\n", + "## Access the data\n", + "* How to retrieve the data\n", + "* Data path and file name conventions\n", + "* Load data\n", + "## Explore the data\n", + "* NetCDF Data structure\n", + "* Xarray essentials\n", + "* Xarray Variable\n", + "## Plot the data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "import random\n", + "\n", + "import glob\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "2372d5be", + "metadata": {}, + "source": [ + "## Access the data" + ] + }, + { + "cell_type": "markdown", + "id": "5b0d9684", + "metadata": {}, + "source": [ + "### How to retrieve the data\n", + "We assume the path \"/data/archive\" is available where you are running this notebook. You can use `os.path.exists(\"/data/archive\")` to verify if the path exists at your machine.\n", + "\n", + "\n", + "### Data path and file name conventions\n", + "There several common terminologies regarding ARM data, for example, data-stream-name, data-level, site, facility, instrument, etc. (For more details, please see [ARM Data File Standards Version 1.2](https://www.google.com/search?q=arm+datastream+facility+cite+definition&rlz=1C1GCEJ_enUS1029US1029&ei=hb41ZICrBPukqtsPvMODIA&ved=0ahUKEwjAgeiS0aL-AhV7kmoFHbzhAAQQ4dUDCBA&uact=5&oq=arm+datastream+facility+cite+definition&gs_lcp=Cgxnd3Mtd2l6LXNlcnAQAzIFCAAQogQyBQgAEKIEMgUIABCiBDIFCAAQogQyBQgAEKIEOgoIABBHENYEELADOgoIIRCgARDDBBAKSgQIQRgAUJ4DWIoOYO0PaAFwAXgAgAGMAYgBuQmSAQMyLjmYAQCgAQHIAQjAAQE&sclient=gws-wiz-serp).) \n", + "\n", + "For example, this notebook is called `aeri01prof3feltz.c1`, where `aeri01prof3feltz` is the \"datastream name\", and the `{process.ds_class_level}}` is the \"data level\".\n", + "\n", + "This datastream also contains site `sgp` and facility `C1`. (Note: individual datastream might have multiple site-facility pairs.)\n", + "In such a case, the data of this data-stream is stored at `/data/archive/sgp/sgpaeri01prof3feltzC1.c1`, which is in the format of `//.`. We can use the following method to assign the data-stream directory `datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )`\n", + "\n", + "The data files under datastream_dir also follows naming conventions. But once reach the datastream_dir level, the most import file naming convention to differentiate the files is \"yyyyMMdd.hhmmss\", which comes handy to filter out files based on datetime. For example, we can use `glob.glob(f'{datastream_dir}/*.200709*.*')` to filter files in 2007 September.\n", + "\n", + "(Note: refer to the https://adc.arm.gov/discovery/#/ and https://adc.arm.gov/solr8/metadata/select API to explore ARM datastream and assoicated available sites and facilities\n", + "\n", + "Please see the following examples in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d7e9eb85", + "metadata": {}, + "outputs": [], + "source": [ + "# Verify if DATA_DIR path exists\n", + "DATA_DIR = \"/data/archive\"\n", + "os.path.exists(DATA_DIR)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "586993fd", + "metadata": {}, + "outputs": [], + "source": [ + "# Speicify datastream_dir following the path conventions and check its existence\n", + "DATASTREAM_NAME = \"aeri01prof3feltz\"\n", + "DATA_LEVEL = \"c1\"\n", + "site = \"sgp\"\n", + "facility = \"C1\"\n", + "datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )\n", + "print(datastream_dir)\n", + "print(os.path.exists(datastream_dir))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0742f7c1", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: list 5 (random) files under datastream_dir\n", + "files = os.listdir(datastream_dir)\n", + "files[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39b98a36", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: get most recent file\n", + "list_of_files = glob.glob(f\"{datastream_dir}/*\") # * means all if need specific format then *.csv\n", + "latest_file = max(list_of_files, key=os.path.getctime)\n", + "latest_file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "902d514e", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus sort datastream files based on datetime\n", + "files = os.listdir(datastream_dir)\n", + "file_sorted = files.copy()\n", + "file_sorted.sort() \n", + "print(file_sorted[:5])\n", + "\n", + "# to reverse\n", + "file_sorted_reverse = files.copy() \n", + "file_sorted_reverse.sort(reverse=True)\n", + "print(file_sorted_reverse[:5])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec5923b2", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: pattern matching\n", + "# filter the 200709** files under datastream_dir\n", + "files_filter = glob.glob(f'{datastream_dir}/*.200709*.*')\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c4a4aa18", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: use armnotebook_utils.file_filter (TODO) to filter files based on datastream info \n" + ] + }, + { + "cell_type": "markdown", + "id": "cfeb9efc", + "metadata": {}, + "source": [ + "### Load data\n", + "The arm data is stored in [NetCDF](https://en.wikipedia.org/wiki/NetCDF#:~:text=%22NetCDF%20(network%20Common%20Data%20Form,format%20for%20representing%20scientific%20data.) format. We can use xarray's `open_dataset` method to load single file, or `open_mfdataset` to open multiple files. (Note: the latter will still return a single xarray dataset object by combining multiple files.)\n", + "\n", + "Note: open_dataset keeps the file handle open and lazy loads its contents. All parameters are passed directly to open_dataset. It is a preferable method over load_dataset for memory-effiency. (more details, please see [xarray.open_dataset](https://docs.xarray.dev/en/stable/generated/xarray.open_dataset.html))\n", + "\n", + "See the following example in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a440a329", + "metadata": {}, + "outputs": [], + "source": [ + "# open single file\n", + "full_path = latest_file\n", + "print(full_path)\n", + "ds_single = xr.open_dataset(full_path)\n", + "ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0143a3d", + "metadata": {}, + "outputs": [], + "source": [ + "print(ds_single)\n", + "print(type(ds_single))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c0f8939", + "metadata": {}, + "outputs": [], + "source": [ + "# open multiple files\n", + "n_files = 3\n", + "full_paths = [os.path.join(datastream_dir, f_path) for f_path in file_sorted_reverse[:n_files]]\n", + "print(full_paths)\n", + "try: # Note: sometimes multiple files cannot be merged, so we used try except here.\n", + " ds_mutiple = xr.open_mfdataset(full_paths)\n", + " # ds_mutiple\n", + " print(type(ds_mutiple))\n", + " print(ds_mutiple)\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "3fa59943", + "metadata": {}, + "source": [ + "## Explore the data" + ] + }, + { + "cell_type": "markdown", + "id": "571f69b9", + "metadata": {}, + "source": [ + "### NetCDF Data structure \n", + "\n", + "\n", + "(If you are confident with NetCDF basics and xarray essentials, feel free to skip this session.)\n", + "Before we dive into data exploration, there are some eseential concpets we should be familiar with (shown below): \n", + "* Dataset\n", + "* Data array\n", + "* Variable\n", + "* Dimenssion\n", + "* Coordinate\n", + "* Data Type\n", + "* Meta Data (Attributes)\n", + "\n", + "We will not go into details about NetCDF basics and here are some references you might find helpful\n", + "* [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#)\n", + "* [Network Common Data Form (NetCDF)](https://www.unidata.ucar.edu/software/netcdf/) and [A Brief History of (netCDF) Time](https://www.unidata.ucar.edu/software/netcdf/time/recs.html)\n", + "\n", + "\n", + "\n", + "### Xarray essentials\n", + "Earlier we introduced the xarray pacakge and used `open_dataset` and `open_mfdataset` to retrieve NetCDF data file as an xarray Dataset object. Recall that you can review the data in a notebook by using `print(ds)` or `ds`. \n", + "\n", + "Using xarray to retrieve the aforementioned NetCDF basics is straightforwared. In general,\n", + "* Dataset: `ds`\n", + "* Data array: `ds.variables`\n", + "* Variable: `ds.ds.variables`\n", + "* Dimenssion: `ds.dims`\n", + "* Coordinate: `ds.coords`\n", + "* Data Type: `type`\n", + "* Meta Data (Attributes)\n", + "\n", + "Also, here are some references if you are new to xarray\n", + "* [Xarray in 45 minutes](https://tutorial.xarray.dev/overview/xarray-in-45-min.html)\n", + "* [Handling NetCDF Files using XArray for Absolute Beginners](https://towardsdatascience.com/handling-netcdf-files-using-xarray-for-absolute-beginners-111a8ab4463f)\n", + "\n", + "Try the following commands in action\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b3eac323", + "metadata": {}, + "outputs": [], + "source": [ + "ds = ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77ecf85d", + "metadata": {}, + "outputs": [], + "source": [ + "# Dataset \n", + "ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25e7de09", + "metadata": {}, + "outputs": [], + "source": [ + "# Data array, Variable\n", + "# Note: the info can be overwhelming. Do not attempt to grasp everything at the first glance.\n", + "ds.variables" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c41b67e", + "metadata": {}, + "outputs": [], + "source": [ + "# dimenssions\n", + "ds.dims" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "156f1dfc", + "metadata": {}, + "outputs": [], + "source": [ + "# coordinates\n", + "ds.coords" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "277d6064", + "metadata": {}, + "outputs": [], + "source": [ + "# Meta Data (Attributes)\n", + "ds.attrs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d334681f", + "metadata": {}, + "outputs": [], + "source": [ + "# type\n", + "print(type(ds))\n", + "print(type(ds.variables))\n", + "print(type(ds.dims))\n", + "print(type(ds.coords))\n", + "print(type(ds.attrs))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "643399d6", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: retrieve variable names only\n", + "list(ds.variables)" + ] + }, + { + "cell_type": "markdown", + "id": "1cf36878", + "metadata": {}, + "source": [ + "#### Discussion: variable vs. coordinates vs. dimenssions. \n", + "From [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#):\n", + "\n", + "* Dimession: A dimension may be used to represent a real physical dimension, for example, time, latitude, longitude, or height. A dimension might also be used to index other quantities, for example station or model-run-number.\n", + "A netCDF dimension has both a name and a length. A dimension length is an arbitrary positive integer, except that one dimension in a netCDF dataset can have the length UNLIMITED.\n", + "\n", + "* Variables: Variables are used to store the bulk of the data in a netCDF dataset. A variable represents an array of values of the same type. A scalar value is treated as a 0-dimensional array. A variable has a name, a data type, and a shape described by its list of dimensions specified when the variable is created. A variable may also have associated attributes, which may be added, deleted or changed after the variable is created.\n", + "\n", + "* Coordinate (Variables): It is legal for a variable to have the same name as a dimension. Such variables have no special meaning to the netCDF library. However there is a convention that such variables should be treated in a special way by software using this library.\n", + "A variable with the same name as a dimension is called a coordinate variable. It typically defines a physical coordinate corresponding to that dimension.\n", + "\n", + "\n", + "Tips: By definition, coordinates and dimenssions are also variable. For example ds.time is a coordinate but it is also a special variable. But in practice, when we talk about variable, it implies regular/non-coordinate variable. We will use this convention for the remaining notebook. " + ] + }, + { + "cell_type": "markdown", + "id": "d47120f9", + "metadata": {}, + "source": [ + "### Xarray Variable\n", + "\n", + "\n", + "We can use `ds.variables` to access varialbes of a dataset. But many times viewing print-out the whole datasets can be overwhemling. Instead, we would work on the individual variable (also called Xarray data array.) using the `ds[var_name]` syntax, where `var_name` is the name of the variable. See the following examples in action.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5a57e136", + "metadata": {}, + "outputs": [], + "source": [ + "print(type(ds[\"time\"]))\n", + "ds[\"time\"]" + ] + }, + { + "cell_type": "markdown", + "id": "51ec643e", + "metadata": {}, + "source": [ + "#### Variable properties\n", + "For individual varible, the following properties are mostly used with the assocaited xarray method to retrieve them. (assuming var = ds[var_name])\n", + "* name: `var.name`\n", + "* data content: `var.data`\n", + "* attributes: `var.attrs`\n", + "* dimenstions: `var.dims`\n", + "* data type: `var.data.dtype`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a632a525", + "metadata": {}, + "outputs": [], + "source": [ + "var_name = \"time\"\n", + "var = ds[var_name]\n", + "\n", + "print(\"var.name: \\n\", var.name, \"\\n\")\n", + "print(\"var.data: \\n\", var.data, \"\\n\")\n", + "print(\"var.attrs: \\n\", var.attrs, \"\\n\")\n", + "print(\"var.dims: \\n\", var.dims, \"\\n\")\n", + "print(\"var.data.dtype: \\n\", var.data.dtype, \"\\n\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b256b07f", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: indivdual variable info table (collect and display variable info as a tabular format)\n", + "df_info = pd.DataFrame()\n", + "df_info[\"var_name\"] = list(ds.variables)\n", + "df_info[\"dims\"] = df_info.var_name.apply(lambda x: ds[x].dims)\n", + "df_info[\"is_dim\"] = df_info.var_name.apply(lambda x: x in ds.dims)\n", + "df_info[\"n_dim\"] = df_info.var_name.apply(lambda x: len(ds[x].dims))\n", + "df_info[\"attrs\"] = df_info.var_name.apply(lambda x: ds[x].attrs)\n", + "df_info[\"dtype\"] = df_info.var_name.apply(lambda x: ds[x].data.dtype)\n", + "df_info" + ] + }, + { + "cell_type": "markdown", + "id": "e6a36b96", + "metadata": {}, + "source": [ + "### Data cleaning/Preprocessing (skipped)\n", + "Data cleaning and preprocessing is an important stage in the the data analysis pipeline. However it will be out of the scope of this notebook. For more information about data cleaning and preprocessing basics, here are some references. \n", + "* [Xarray Fundamentals](https://earth-env-data-science.github.io/lectures/xarray/xarray.html)\n", + "* [Xarray Tutorial — Pangeo Gallery documentation](http://gallery.pangeo.io/repos/pangeo-data/pangeo-tutorial-gallery/xarray.html)\n", + "* [Pythonic Data Cleaning With pandas and NumPy](https://realpython.com/python-data-cleaning-numpy-pandas/)\n", + "* [Pandas - Cleaning Data](https://www.w3schools.com/python/pandas/pandas_cleaning.asp)" + ] + }, + { + "cell_type": "markdown", + "id": "ae6e1a69", + "metadata": {}, + "source": [ + "### Plotting\n", + "Data visualization (or plotting) is another important data analysis topic and deserves its own discussion. Here in this notebook we will only demonstrate basic tool for simple data visualization tasks.\n", + "\n", + "Here are some reference you might find useful:\n", + "* xarray plotting: https://docs.xarray.dev/en/stable/user-guide/plotting.html\n", + "* Atmospheric Community Toolkit (ACT): https://arm-doe.github.io/ACT/index.html\n", + "\n", + "Note: this notebook is auto-generated using a template. It uses a general idea to select variable(s) to plot and is not customized to each indivdual notebook. Feel free to change the variables in intrrests, especially certain figure is failed to plot." + ] + }, + { + "cell_type": "markdown", + "id": "7e417fb2", + "metadata": {}, + "source": [ + "#### 1-dimenssional basic time series plot\n", + "\n", + "For the following plot we would like to find variables such that\n", + "* it has one and only one dimession\n", + "* \"time\" is its coordinate variable\n", + "* it is not a dimenssion itself,\n", + "* it is not a special variable with substrings within [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "\n", + "Please see the following example in action. (For more details about pandas filtering, please see the following references.)\n", + "* [pandas: multiple conditions while indexing data frame](https://stackoverflow.com/questions/22591174/pandas-multiple-conditions-while-indexing-data-frame-unexpected-behavior)\n", + "* [pandas.DataFrame.apply](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.apply.html)\n", + "* [How to test if a string contains one of the substrings in a list...](https://stackoverflow.com/questions/26577516/how-to-test-if-a-string-contains-one-of-the-substrings-in-a-list-in-pandas" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30edac2d", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==1, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter = df_info[(df_info.n_dim == 1) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4777a659", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_1d = df_filter.var_name.values[0]\n", + " var_1d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb5ae985", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " ds[var_1d].plot()\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "9e41066e", + "metadata": {}, + "source": [ + "#### 2-dimenssional basic plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09bd4adc", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==2, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_2 = df_info[(df_info.n_dim == 2) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2cdea1a9", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_2d = df_filter_2.var_name.values[0]\n", + " var_2d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5f48879", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " print(ds[var_2d].dims)\n", + " # ds[var_2d].plot()\n", + "\n", + " # conventionally, use \"time\" as x-axis\n", + " ds[var_2d].plot(x=\"time\")\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "7554b2a6", + "metadata": {}, + "source": [ + "#### qc-plotting (optional)\n", + "\n", + "Note: act qc plotting has more strict requirement, one of them is the associated qc variable need to have \"flag_masks\" attributes. Which is added by using ds = act.io.armfiles.read_netcdf(files_list), then ds.clean.cleanup()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71c2096f", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " ds_act = act.io.armfiles.read_netcdf(full_path)\n", + " print(type(ds_act))\n", + " ds_act.clean.cleanup()\n", + "\n", + " # or \n", + " # ds.clean.cleanup()\n", + "except Exception as e:\n", + " print(\"ERROR\", e)\n", + " ds_act = ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "844f8505", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter valid variables for ACT qc plotting\n", + "condition = (\"qc_\" + df_info.var_name).apply(lambda x: ds_act[x].attrs.get(\"flag_masks\") is not None \n", + " if x in list(ds.variables) else False)\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_3 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings))) &\n", + " condition\n", + " ]\n", + "df_filter_3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd38cc6", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "try:\n", + " qc_variable = df_filter_3.var_name.values[0]\n", + " print(qc_variable)\n", + "except Exception as e:\n", + " print(e)\n", + " \n", + "try:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + " qc_ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "d76e4d27", + "metadata": {}, + "source": [ + "#### bonus: choose variables to plot from a dropdown menu " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb733804", + "metadata": {}, + "outputs": [], + "source": [ + "# Valid variables filtering\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_4 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82d5c20d", + "metadata": {}, + "outputs": [], + "source": [ + "# example 1: using xarray plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2fc9aaa4", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# fig, ax = plt.subplots(figsize=(10, 4))\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "# fig.clear() # Remove old lines from plot and plot new one\n", + "# if len(ds[var].dims)==2:\n", + "# ds[var].plot(x=\"time\", add_colorbar=False)\n", + "# else:\n", + "# ds[var].plot()\n", + "# plt.grid()\n", + "# plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fabdd802", + "metadata": {}, + "outputs": [], + "source": [ + "# example 2: using act plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "962b4186", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "\n", + "\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "\n", + "# i_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + "# i_display.add_subplots((1,), figsize=(10, 4))\n", + "# ax = i_display.plot(var, subplot_index=(0,), set_title=f\"{var} ({ds_act[var].attrs['long_name']})\",)\n", + "\n", + "# ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "# ax.grid()\n", + "# plt.show()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/AERIPROF/aeri01prof3feltz.c1.ipynb b/VAPs/quicklook/AERIPROF/aeri01prof3feltz.c1.ipynb new file mode 100644 index 00000000..4513e4a6 --- /dev/null +++ b/VAPs/quicklook/AERIPROF/aeri01prof3feltz.c1.ipynb @@ -0,0 +1,339 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# AERI01PROF3FELTZ.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/aeriprof) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'aeri01prof3feltz'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2014-03-11', 'facility': 'C1', 'site': 'sgp', 'start_date': '2002-04-18'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2014-03-09'\n", + "date_end = '2014-03-11'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['pressure', 'temperature', 'waterVaporMixingRatio']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'pressure'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'pressure'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/AERIPROF/aeriprof3feltz.c1.ipynb b/VAPs/quicklook/AERIPROF/aeriprof3feltz.c1.ipynb new file mode 100644 index 00000000..d7b4348d --- /dev/null +++ b/VAPs/quicklook/AERIPROF/aeriprof3feltz.c1.ipynb @@ -0,0 +1,339 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# AERIPROF3FELTZ.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/aeriprof) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'aeriprof3feltz'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2023-12-17', 'facility': 'C1', 'site': 'sgp', 'start_date': '2007-01-01'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2023-12-15'\n", + "date_end = '2023-12-17'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['pressure', 'temperature', 'waterVaporMixingRatio']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'pressure'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'pressure'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/AERIPROF/qmeaeriprof.c1.ipynb b/VAPs/quicklook/AERIPROF/qmeaeriprof.c1.ipynb new file mode 100644 index 00000000..4a3bc150 --- /dev/null +++ b/VAPs/quicklook/AERIPROF/qmeaeriprof.c1.ipynb @@ -0,0 +1,297 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# QMEAERIPROF.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/aeriprof) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'qmeaeriprof'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2004-01-26', 'facility': 'C1', 'site': 'sgp', 'start_date': '1996-06-14'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2004-01-26'\n", + "date_end = '2004-01-26'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['temperature_resid', 'dewpoint_resid', 'mixing_ratio_resid']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'temperature_resid'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/AEROSOLBE/AEROSOLBE_tutorial.ipynb b/VAPs/quicklook/AEROSOLBE/AEROSOLBE_tutorial.ipynb new file mode 100644 index 00000000..1dfbc5a5 --- /dev/null +++ b/VAPs/quicklook/AEROSOLBE/AEROSOLBE_tutorial.ipynb @@ -0,0 +1,838 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# AEROSOLBE1TURN.C1 Notebook\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/aerosolbe) for more information about this vap." + ] + }, + { + "cell_type": "markdown", + "id": "97097763", + "metadata": {}, + "source": [ + "In this notebook, we demonstrate the workflow to explore ARM vap data (using aerosolbe1turn as an example.) Value-added products (VAPs) are higher-order data products that have been analyzed and processed to ease scientists’ use of ARM data in atmospheric research and global climate models. \n", + "Here is the main content we will cover." + ] + }, + { + "cell_type": "markdown", + "id": "eddec40f", + "metadata": {}, + "source": [ + "# Table of Content\n", + "## Access the data\n", + "* How to retrieve the data\n", + "* Data path and file name conventions\n", + "* Load data\n", + "## Explore the data\n", + "* NetCDF Data structure\n", + "* Xarray essentials\n", + "* Xarray Variable\n", + "## Plot the data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "import random\n", + "\n", + "import glob\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "2372d5be", + "metadata": {}, + "source": [ + "## Access the data" + ] + }, + { + "cell_type": "markdown", + "id": "5b0d9684", + "metadata": {}, + "source": [ + "### How to retrieve the data\n", + "We assume the path \"/data/archive\" is available where you are running this notebook. You can use `os.path.exists(\"/data/archive\")` to verify if the path exists at your machine.\n", + "\n", + "\n", + "### Data path and file name conventions\n", + "There several common terminologies regarding ARM data, for example, data-stream-name, data-level, site, facility, instrument, etc. (For more details, please see [ARM Data File Standards Version 1.2](https://www.google.com/search?q=arm+datastream+facility+cite+definition&rlz=1C1GCEJ_enUS1029US1029&ei=hb41ZICrBPukqtsPvMODIA&ved=0ahUKEwjAgeiS0aL-AhV7kmoFHbzhAAQQ4dUDCBA&uact=5&oq=arm+datastream+facility+cite+definition&gs_lcp=Cgxnd3Mtd2l6LXNlcnAQAzIFCAAQogQyBQgAEKIEMgUIABCiBDIFCAAQogQyBQgAEKIEOgoIABBHENYEELADOgoIIRCgARDDBBAKSgQIQRgAUJ4DWIoOYO0PaAFwAXgAgAGMAYgBuQmSAQMyLjmYAQCgAQHIAQjAAQE&sclient=gws-wiz-serp).) \n", + "\n", + "For example, this notebook is called `aerosolbe1turn.c1`, where `aerosolbe1turn` is the \"datastream name\", and the `{process.ds_class_level}}` is the \"data level\".\n", + "\n", + "This datastream also contains site `sgp` and facility `C1`. (Note: individual datastream might have multiple site-facility pairs.)\n", + "In such a case, the data of this data-stream is stored at `/data/archive/sgp/sgpaerosolbe1turnC1.c1`, which is in the format of `//.`. We can use the following method to assign the data-stream directory `datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )`\n", + "\n", + "The data files under datastream_dir also follows naming conventions. But once reach the datastream_dir level, the most import file naming convention to differentiate the files is \"yyyyMMdd.hhmmss\", which comes handy to filter out files based on datetime. For example, we can use `glob.glob(f'{datastream_dir}/*.200709*.*')` to filter files in 2007 September.\n", + "\n", + "(Note: refer to the https://adc.arm.gov/discovery/#/ and https://adc.arm.gov/solr8/metadata/select API to explore ARM datastream and assoicated available sites and facilities\n", + "\n", + "Please see the following examples in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d7e9eb85", + "metadata": {}, + "outputs": [], + "source": [ + "# Verify if DATA_DIR path exists\n", + "DATA_DIR = \"/data/archive\"\n", + "os.path.exists(DATA_DIR)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "586993fd", + "metadata": {}, + "outputs": [], + "source": [ + "# Speicify datastream_dir following the path conventions and check its existence\n", + "DATASTREAM_NAME = \"aerosolbe1turn\"\n", + "DATA_LEVEL = \"c1\"\n", + "site = \"sgp\"\n", + "facility = \"C1\"\n", + "datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )\n", + "print(datastream_dir)\n", + "print(os.path.exists(datastream_dir))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0742f7c1", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: list 5 (random) files under datastream_dir\n", + "files = os.listdir(datastream_dir)\n", + "files[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39b98a36", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: get most recent file\n", + "list_of_files = glob.glob(f\"{datastream_dir}/*\") # * means all if need specific format then *.csv\n", + "latest_file = max(list_of_files, key=os.path.getctime)\n", + "latest_file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "902d514e", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus sort datastream files based on datetime\n", + "files = os.listdir(datastream_dir)\n", + "file_sorted = files.copy()\n", + "file_sorted.sort() \n", + "print(file_sorted[:5])\n", + "\n", + "# to reverse\n", + "file_sorted_reverse = files.copy() \n", + "file_sorted_reverse.sort(reverse=True)\n", + "print(file_sorted_reverse[:5])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec5923b2", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: pattern matching\n", + "# filter the 200709** files under datastream_dir\n", + "files_filter = glob.glob(f'{datastream_dir}/*.200709*.*')\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c4a4aa18", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: use armnotebook_utils.file_filter (TODO) to filter files based on datastream info \n" + ] + }, + { + "cell_type": "markdown", + "id": "cfeb9efc", + "metadata": {}, + "source": [ + "### Load data\n", + "The arm data is stored in [NetCDF](https://en.wikipedia.org/wiki/NetCDF#:~:text=%22NetCDF%20(network%20Common%20Data%20Form,format%20for%20representing%20scientific%20data.) format. We can use xarray's `open_dataset` method to load single file, or `open_mfdataset` to open multiple files. (Note: the latter will still return a single xarray dataset object by combining multiple files.)\n", + "\n", + "Note: open_dataset keeps the file handle open and lazy loads its contents. All parameters are passed directly to open_dataset. It is a preferable method over load_dataset for memory-effiency. (more details, please see [xarray.open_dataset](https://docs.xarray.dev/en/stable/generated/xarray.open_dataset.html))\n", + "\n", + "See the following example in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a440a329", + "metadata": {}, + "outputs": [], + "source": [ + "# open single file\n", + "full_path = latest_file\n", + "print(full_path)\n", + "ds_single = xr.open_dataset(full_path)\n", + "ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0143a3d", + "metadata": {}, + "outputs": [], + "source": [ + "print(ds_single)\n", + "print(type(ds_single))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c0f8939", + "metadata": {}, + "outputs": [], + "source": [ + "# open multiple files\n", + "n_files = 3\n", + "full_paths = [os.path.join(datastream_dir, f_path) for f_path in file_sorted_reverse[:n_files]]\n", + "print(full_paths)\n", + "try: # Note: sometimes multiple files cannot be merged, so we used try except here.\n", + " ds_mutiple = xr.open_mfdataset(full_paths)\n", + " # ds_mutiple\n", + " print(type(ds_mutiple))\n", + " print(ds_mutiple)\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "3fa59943", + "metadata": {}, + "source": [ + "## Explore the data" + ] + }, + { + "cell_type": "markdown", + "id": "571f69b9", + "metadata": {}, + "source": [ + "### NetCDF Data structure \n", + "\n", + "\n", + "(If you are confident with NetCDF basics and xarray essentials, feel free to skip this session.)\n", + "Before we dive into data exploration, there are some eseential concpets we should be familiar with (shown below): \n", + "* Dataset\n", + "* Data array\n", + "* Variable\n", + "* Dimenssion\n", + "* Coordinate\n", + "* Data Type\n", + "* Meta Data (Attributes)\n", + "\n", + "We will not go into details about NetCDF basics and here are some references you might find helpful\n", + "* [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#)\n", + "* [Network Common Data Form (NetCDF)](https://www.unidata.ucar.edu/software/netcdf/) and [A Brief History of (netCDF) Time](https://www.unidata.ucar.edu/software/netcdf/time/recs.html)\n", + "\n", + "\n", + "\n", + "### Xarray essentials\n", + "Earlier we introduced the xarray pacakge and used `open_dataset` and `open_mfdataset` to retrieve NetCDF data file as an xarray Dataset object. Recall that you can review the data in a notebook by using `print(ds)` or `ds`. \n", + "\n", + "Using xarray to retrieve the aforementioned NetCDF basics is straightforwared. In general,\n", + "* Dataset: `ds`\n", + "* Data array: `ds.variables`\n", + "* Variable: `ds.ds.variables`\n", + "* Dimenssion: `ds.dims`\n", + "* Coordinate: `ds.coords`\n", + "* Data Type: `type`\n", + "* Meta Data (Attributes)\n", + "\n", + "Also, here are some references if you are new to xarray\n", + "* [Xarray in 45 minutes](https://tutorial.xarray.dev/overview/xarray-in-45-min.html)\n", + "* [Handling NetCDF Files using XArray for Absolute Beginners](https://towardsdatascience.com/handling-netcdf-files-using-xarray-for-absolute-beginners-111a8ab4463f)\n", + "\n", + "Try the following commands in action\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b3eac323", + "metadata": {}, + "outputs": [], + "source": [ + "ds = ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77ecf85d", + "metadata": {}, + "outputs": [], + "source": [ + "# Dataset \n", + "ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25e7de09", + "metadata": {}, + "outputs": [], + "source": [ + "# Data array, Variable\n", + "# Note: the info can be overwhelming. Do not attempt to grasp everything at the first glance.\n", + "ds.variables" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c41b67e", + "metadata": {}, + "outputs": [], + "source": [ + "# dimenssions\n", + "ds.dims" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "156f1dfc", + "metadata": {}, + "outputs": [], + "source": [ + "# coordinates\n", + "ds.coords" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "277d6064", + "metadata": {}, + "outputs": [], + "source": [ + "# Meta Data (Attributes)\n", + "ds.attrs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d334681f", + "metadata": {}, + "outputs": [], + "source": [ + "# type\n", + "print(type(ds))\n", + "print(type(ds.variables))\n", + "print(type(ds.dims))\n", + "print(type(ds.coords))\n", + "print(type(ds.attrs))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "643399d6", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: retrieve variable names only\n", + "list(ds.variables)" + ] + }, + { + "cell_type": "markdown", + "id": "1cf36878", + "metadata": {}, + "source": [ + "#### Discussion: variable vs. coordinates vs. dimenssions. \n", + "From [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#):\n", + "\n", + "* Dimession: A dimension may be used to represent a real physical dimension, for example, time, latitude, longitude, or height. A dimension might also be used to index other quantities, for example station or model-run-number.\n", + "A netCDF dimension has both a name and a length. A dimension length is an arbitrary positive integer, except that one dimension in a netCDF dataset can have the length UNLIMITED.\n", + "\n", + "* Variables: Variables are used to store the bulk of the data in a netCDF dataset. A variable represents an array of values of the same type. A scalar value is treated as a 0-dimensional array. A variable has a name, a data type, and a shape described by its list of dimensions specified when the variable is created. A variable may also have associated attributes, which may be added, deleted or changed after the variable is created.\n", + "\n", + "* Coordinate (Variables): It is legal for a variable to have the same name as a dimension. Such variables have no special meaning to the netCDF library. However there is a convention that such variables should be treated in a special way by software using this library.\n", + "A variable with the same name as a dimension is called a coordinate variable. It typically defines a physical coordinate corresponding to that dimension.\n", + "\n", + "\n", + "Tips: By definition, coordinates and dimenssions are also variable. For example ds.time is a coordinate but it is also a special variable. But in practice, when we talk about variable, it implies regular/non-coordinate variable. We will use this convention for the remaining notebook. " + ] + }, + { + "cell_type": "markdown", + "id": "d47120f9", + "metadata": {}, + "source": [ + "### Xarray Variable\n", + "\n", + "\n", + "We can use `ds.variables` to access varialbes of a dataset. But many times viewing print-out the whole datasets can be overwhemling. Instead, we would work on the individual variable (also called Xarray data array.) using the `ds[var_name]` syntax, where `var_name` is the name of the variable. See the following examples in action.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5a57e136", + "metadata": {}, + "outputs": [], + "source": [ + "print(type(ds[\"time\"]))\n", + "ds[\"time\"]" + ] + }, + { + "cell_type": "markdown", + "id": "51ec643e", + "metadata": {}, + "source": [ + "#### Variable properties\n", + "For individual varible, the following properties are mostly used with the assocaited xarray method to retrieve them. (assuming var = ds[var_name])\n", + "* name: `var.name`\n", + "* data content: `var.data`\n", + "* attributes: `var.attrs`\n", + "* dimenstions: `var.dims`\n", + "* data type: `var.data.dtype`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a632a525", + "metadata": {}, + "outputs": [], + "source": [ + "var_name = \"time\"\n", + "var = ds[var_name]\n", + "\n", + "print(\"var.name: \\n\", var.name, \"\\n\")\n", + "print(\"var.data: \\n\", var.data, \"\\n\")\n", + "print(\"var.attrs: \\n\", var.attrs, \"\\n\")\n", + "print(\"var.dims: \\n\", var.dims, \"\\n\")\n", + "print(\"var.data.dtype: \\n\", var.data.dtype, \"\\n\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b256b07f", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: indivdual variable info table (collect and display variable info as a tabular format)\n", + "df_info = pd.DataFrame()\n", + "df_info[\"var_name\"] = list(ds.variables)\n", + "df_info[\"dims\"] = df_info.var_name.apply(lambda x: ds[x].dims)\n", + "df_info[\"is_dim\"] = df_info.var_name.apply(lambda x: x in ds.dims)\n", + "df_info[\"n_dim\"] = df_info.var_name.apply(lambda x: len(ds[x].dims))\n", + "df_info[\"attrs\"] = df_info.var_name.apply(lambda x: ds[x].attrs)\n", + "df_info[\"dtype\"] = df_info.var_name.apply(lambda x: ds[x].data.dtype)\n", + "df_info" + ] + }, + { + "cell_type": "markdown", + "id": "e6a36b96", + "metadata": {}, + "source": [ + "### Data cleaning/Preprocessing (skipped)\n", + "Data cleaning and preprocessing is an important stage in the the data analysis pipeline. However it will be out of the scope of this notebook. For more information about data cleaning and preprocessing basics, here are some references. \n", + "* [Xarray Fundamentals](https://earth-env-data-science.github.io/lectures/xarray/xarray.html)\n", + "* [Xarray Tutorial — Pangeo Gallery documentation](http://gallery.pangeo.io/repos/pangeo-data/pangeo-tutorial-gallery/xarray.html)\n", + "* [Pythonic Data Cleaning With pandas and NumPy](https://realpython.com/python-data-cleaning-numpy-pandas/)\n", + "* [Pandas - Cleaning Data](https://www.w3schools.com/python/pandas/pandas_cleaning.asp)" + ] + }, + { + "cell_type": "markdown", + "id": "ae6e1a69", + "metadata": {}, + "source": [ + "### Plotting\n", + "Data visualization (or plotting) is another important data analysis topic and deserves its own discussion. Here in this notebook we will only demonstrate basic tool for simple data visualization tasks.\n", + "\n", + "Here are some reference you might find useful:\n", + "* xarray plotting: https://docs.xarray.dev/en/stable/user-guide/plotting.html\n", + "* Atmospheric Community Toolkit (ACT): https://arm-doe.github.io/ACT/index.html\n", + "\n", + "Note: this notebook is auto-generated using a template. It uses a general idea to select variable(s) to plot and is not customized to each indivdual notebook. Feel free to change the variables in intrrests, especially certain figure is failed to plot." + ] + }, + { + "cell_type": "markdown", + "id": "7e417fb2", + "metadata": {}, + "source": [ + "#### 1-dimenssional basic time series plot\n", + "\n", + "For the following plot we would like to find variables such that\n", + "* it has one and only one dimession\n", + "* \"time\" is its coordinate variable\n", + "* it is not a dimenssion itself,\n", + "* it is not a special variable with substrings within [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "\n", + "Please see the following example in action. (For more details about pandas filtering, please see the following references.)\n", + "* [pandas: multiple conditions while indexing data frame](https://stackoverflow.com/questions/22591174/pandas-multiple-conditions-while-indexing-data-frame-unexpected-behavior)\n", + "* [pandas.DataFrame.apply](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.apply.html)\n", + "* [How to test if a string contains one of the substrings in a list...](https://stackoverflow.com/questions/26577516/how-to-test-if-a-string-contains-one-of-the-substrings-in-a-list-in-pandas" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30edac2d", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==1, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter = df_info[(df_info.n_dim == 1) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4777a659", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_1d = df_filter.var_name.values[0]\n", + " var_1d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb5ae985", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " ds[var_1d].plot()\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "9e41066e", + "metadata": {}, + "source": [ + "#### 2-dimenssional basic plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09bd4adc", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==2, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_2 = df_info[(df_info.n_dim == 2) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2cdea1a9", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_2d = df_filter_2.var_name.values[0]\n", + " var_2d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5f48879", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " print(ds[var_2d].dims)\n", + " # ds[var_2d].plot()\n", + "\n", + " # conventionally, use \"time\" as x-axis\n", + " ds[var_2d].plot(x=\"time\")\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "7554b2a6", + "metadata": {}, + "source": [ + "#### qc-plotting (optional)\n", + "\n", + "Note: act qc plotting has more strict requirement, one of them is the associated qc variable need to have \"flag_masks\" attributes. Which is added by using ds = act.io.armfiles.read_netcdf(files_list), then ds.clean.cleanup()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71c2096f", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " ds_act = act.io.armfiles.read_netcdf(full_path)\n", + " print(type(ds_act))\n", + " ds_act.clean.cleanup()\n", + "\n", + " # or \n", + " # ds.clean.cleanup()\n", + "except Exception as e:\n", + " print(\"ERROR\", e)\n", + " ds_act = ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "844f8505", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter valid variables for ACT qc plotting\n", + "condition = (\"qc_\" + df_info.var_name).apply(lambda x: ds_act[x].attrs.get(\"flag_masks\") is not None \n", + " if x in list(ds.variables) else False)\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_3 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings))) &\n", + " condition\n", + " ]\n", + "df_filter_3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd38cc6", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "try:\n", + " qc_variable = df_filter_3.var_name.values[0]\n", + " print(qc_variable)\n", + "except Exception as e:\n", + " print(e)\n", + " \n", + "try:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + " qc_ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "d76e4d27", + "metadata": {}, + "source": [ + "#### bonus: choose variables to plot from a dropdown menu " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb733804", + "metadata": {}, + "outputs": [], + "source": [ + "# Valid variables filtering\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_4 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82d5c20d", + "metadata": {}, + "outputs": [], + "source": [ + "# example 1: using xarray plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2fc9aaa4", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# fig, ax = plt.subplots(figsize=(10, 4))\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "# fig.clear() # Remove old lines from plot and plot new one\n", + "# if len(ds[var].dims)==2:\n", + "# ds[var].plot(x=\"time\", add_colorbar=False)\n", + "# else:\n", + "# ds[var].plot()\n", + "# plt.grid()\n", + "# plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fabdd802", + "metadata": {}, + "outputs": [], + "source": [ + "# example 2: using act plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "962b4186", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "\n", + "\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "\n", + "# i_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + "# i_display.add_subplots((1,), figsize=(10, 4))\n", + "# ax = i_display.plot(var, subplot_index=(0,), set_title=f\"{var} ({ds_act[var].attrs['long_name']})\",)\n", + "\n", + "# ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "# ax.grid()\n", + "# plt.show()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/AEROSOLBE/aerosolbe1turn.c1.ipynb b/VAPs/quicklook/AEROSOLBE/aerosolbe1turn.c1.ipynb new file mode 100644 index 00000000..cc96202b --- /dev/null +++ b/VAPs/quicklook/AEROSOLBE/aerosolbe1turn.c1.ipynb @@ -0,0 +1,339 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# AEROSOLBE1TURN.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/aerosolbe) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'aerosolbe1turn'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2021-04-30', 'facility': 'C1', 'site': 'sgp', 'start_date': '2001-01-01'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2021-03-30'\n", + "date_end = '2021-04-01'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['be_aod_500', 'be_aod_355', 'be_angstrom_exponent']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'be_aod_500'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'be_aod_500'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/AIP/AIP_tutorial.ipynb b/VAPs/quicklook/AIP/AIP_tutorial.ipynb new file mode 100644 index 00000000..091e5bf8 --- /dev/null +++ b/VAPs/quicklook/AIP/AIP_tutorial.ipynb @@ -0,0 +1,838 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# AIP1OGREN.C1 Notebook\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/aip) for more information about this vap." + ] + }, + { + "cell_type": "markdown", + "id": "97097763", + "metadata": {}, + "source": [ + "In this notebook, we demonstrate the workflow to explore ARM vap data (using aip1ogren as an example.) Value-added products (VAPs) are higher-order data products that have been analyzed and processed to ease scientists’ use of ARM data in atmospheric research and global climate models. \n", + "Here is the main content we will cover." + ] + }, + { + "cell_type": "markdown", + "id": "eddec40f", + "metadata": {}, + "source": [ + "# Table of Content\n", + "## Access the data\n", + "* How to retrieve the data\n", + "* Data path and file name conventions\n", + "* Load data\n", + "## Explore the data\n", + "* NetCDF Data structure\n", + "* Xarray essentials\n", + "* Xarray Variable\n", + "## Plot the data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "import random\n", + "\n", + "import glob\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "2372d5be", + "metadata": {}, + "source": [ + "## Access the data" + ] + }, + { + "cell_type": "markdown", + "id": "5b0d9684", + "metadata": {}, + "source": [ + "### How to retrieve the data\n", + "We assume the path \"/data/archive\" is available where you are running this notebook. You can use `os.path.exists(\"/data/archive\")` to verify if the path exists at your machine.\n", + "\n", + "\n", + "### Data path and file name conventions\n", + "There several common terminologies regarding ARM data, for example, data-stream-name, data-level, site, facility, instrument, etc. (For more details, please see [ARM Data File Standards Version 1.2](https://www.google.com/search?q=arm+datastream+facility+cite+definition&rlz=1C1GCEJ_enUS1029US1029&ei=hb41ZICrBPukqtsPvMODIA&ved=0ahUKEwjAgeiS0aL-AhV7kmoFHbzhAAQQ4dUDCBA&uact=5&oq=arm+datastream+facility+cite+definition&gs_lcp=Cgxnd3Mtd2l6LXNlcnAQAzIFCAAQogQyBQgAEKIEMgUIABCiBDIFCAAQogQyBQgAEKIEOgoIABBHENYEELADOgoIIRCgARDDBBAKSgQIQRgAUJ4DWIoOYO0PaAFwAXgAgAGMAYgBuQmSAQMyLjmYAQCgAQHIAQjAAQE&sclient=gws-wiz-serp).) \n", + "\n", + "For example, this notebook is called `aip1ogren.c1`, where `aip1ogren` is the \"datastream name\", and the `{process.ds_class_level}}` is the \"data level\".\n", + "\n", + "This datastream also contains site `hfe` and facility `M1`. (Note: individual datastream might have multiple site-facility pairs.)\n", + "In such a case, the data of this data-stream is stored at `/data/archive/hfe/hfeaip1ogrenM1.c1`, which is in the format of `//.`. We can use the following method to assign the data-stream directory `datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )`\n", + "\n", + "The data files under datastream_dir also follows naming conventions. But once reach the datastream_dir level, the most import file naming convention to differentiate the files is \"yyyyMMdd.hhmmss\", which comes handy to filter out files based on datetime. For example, we can use `glob.glob(f'{datastream_dir}/*.200709*.*')` to filter files in 2007 September.\n", + "\n", + "(Note: refer to the https://adc.arm.gov/discovery/#/ and https://adc.arm.gov/solr8/metadata/select API to explore ARM datastream and assoicated available sites and facilities\n", + "\n", + "Please see the following examples in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d7e9eb85", + "metadata": {}, + "outputs": [], + "source": [ + "# Verify if DATA_DIR path exists\n", + "DATA_DIR = \"/data/archive\"\n", + "os.path.exists(DATA_DIR)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "586993fd", + "metadata": {}, + "outputs": [], + "source": [ + "# Speicify datastream_dir following the path conventions and check its existence\n", + "DATASTREAM_NAME = \"aip1ogren\"\n", + "DATA_LEVEL = \"c1\"\n", + "site = \"hfe\"\n", + "facility = \"M1\"\n", + "datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )\n", + "print(datastream_dir)\n", + "print(os.path.exists(datastream_dir))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0742f7c1", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: list 5 (random) files under datastream_dir\n", + "files = os.listdir(datastream_dir)\n", + "files[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39b98a36", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: get most recent file\n", + "list_of_files = glob.glob(f\"{datastream_dir}/*\") # * means all if need specific format then *.csv\n", + "latest_file = max(list_of_files, key=os.path.getctime)\n", + "latest_file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "902d514e", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus sort datastream files based on datetime\n", + "files = os.listdir(datastream_dir)\n", + "file_sorted = files.copy()\n", + "file_sorted.sort() \n", + "print(file_sorted[:5])\n", + "\n", + "# to reverse\n", + "file_sorted_reverse = files.copy() \n", + "file_sorted_reverse.sort(reverse=True)\n", + "print(file_sorted_reverse[:5])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec5923b2", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: pattern matching\n", + "# filter the 200709** files under datastream_dir\n", + "files_filter = glob.glob(f'{datastream_dir}/*.200709*.*')\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c4a4aa18", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: use armnotebook_utils.file_filter (TODO) to filter files based on datastream info \n" + ] + }, + { + "cell_type": "markdown", + "id": "cfeb9efc", + "metadata": {}, + "source": [ + "### Load data\n", + "The arm data is stored in [NetCDF](https://en.wikipedia.org/wiki/NetCDF#:~:text=%22NetCDF%20(network%20Common%20Data%20Form,format%20for%20representing%20scientific%20data.) format. We can use xarray's `open_dataset` method to load single file, or `open_mfdataset` to open multiple files. (Note: the latter will still return a single xarray dataset object by combining multiple files.)\n", + "\n", + "Note: open_dataset keeps the file handle open and lazy loads its contents. All parameters are passed directly to open_dataset. It is a preferable method over load_dataset for memory-effiency. (more details, please see [xarray.open_dataset](https://docs.xarray.dev/en/stable/generated/xarray.open_dataset.html))\n", + "\n", + "See the following example in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a440a329", + "metadata": {}, + "outputs": [], + "source": [ + "# open single file\n", + "full_path = latest_file\n", + "print(full_path)\n", + "ds_single = xr.open_dataset(full_path)\n", + "ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0143a3d", + "metadata": {}, + "outputs": [], + "source": [ + "print(ds_single)\n", + "print(type(ds_single))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c0f8939", + "metadata": {}, + "outputs": [], + "source": [ + "# open multiple files\n", + "n_files = 3\n", + "full_paths = [os.path.join(datastream_dir, f_path) for f_path in file_sorted_reverse[:n_files]]\n", + "print(full_paths)\n", + "try: # Note: sometimes multiple files cannot be merged, so we used try except here.\n", + " ds_mutiple = xr.open_mfdataset(full_paths)\n", + " # ds_mutiple\n", + " print(type(ds_mutiple))\n", + " print(ds_mutiple)\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "3fa59943", + "metadata": {}, + "source": [ + "## Explore the data" + ] + }, + { + "cell_type": "markdown", + "id": "571f69b9", + "metadata": {}, + "source": [ + "### NetCDF Data structure \n", + "\n", + "\n", + "(If you are confident with NetCDF basics and xarray essentials, feel free to skip this session.)\n", + "Before we dive into data exploration, there are some eseential concpets we should be familiar with (shown below): \n", + "* Dataset\n", + "* Data array\n", + "* Variable\n", + "* Dimenssion\n", + "* Coordinate\n", + "* Data Type\n", + "* Meta Data (Attributes)\n", + "\n", + "We will not go into details about NetCDF basics and here are some references you might find helpful\n", + "* [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#)\n", + "* [Network Common Data Form (NetCDF)](https://www.unidata.ucar.edu/software/netcdf/) and [A Brief History of (netCDF) Time](https://www.unidata.ucar.edu/software/netcdf/time/recs.html)\n", + "\n", + "\n", + "\n", + "### Xarray essentials\n", + "Earlier we introduced the xarray pacakge and used `open_dataset` and `open_mfdataset` to retrieve NetCDF data file as an xarray Dataset object. Recall that you can review the data in a notebook by using `print(ds)` or `ds`. \n", + "\n", + "Using xarray to retrieve the aforementioned NetCDF basics is straightforwared. In general,\n", + "* Dataset: `ds`\n", + "* Data array: `ds.variables`\n", + "* Variable: `ds.ds.variables`\n", + "* Dimenssion: `ds.dims`\n", + "* Coordinate: `ds.coords`\n", + "* Data Type: `type`\n", + "* Meta Data (Attributes)\n", + "\n", + "Also, here are some references if you are new to xarray\n", + "* [Xarray in 45 minutes](https://tutorial.xarray.dev/overview/xarray-in-45-min.html)\n", + "* [Handling NetCDF Files using XArray for Absolute Beginners](https://towardsdatascience.com/handling-netcdf-files-using-xarray-for-absolute-beginners-111a8ab4463f)\n", + "\n", + "Try the following commands in action\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b3eac323", + "metadata": {}, + "outputs": [], + "source": [ + "ds = ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77ecf85d", + "metadata": {}, + "outputs": [], + "source": [ + "# Dataset \n", + "ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25e7de09", + "metadata": {}, + "outputs": [], + "source": [ + "# Data array, Variable\n", + "# Note: the info can be overwhelming. Do not attempt to grasp everything at the first glance.\n", + "ds.variables" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c41b67e", + "metadata": {}, + "outputs": [], + "source": [ + "# dimenssions\n", + "ds.dims" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "156f1dfc", + "metadata": {}, + "outputs": [], + "source": [ + "# coordinates\n", + "ds.coords" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "277d6064", + "metadata": {}, + "outputs": [], + "source": [ + "# Meta Data (Attributes)\n", + "ds.attrs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d334681f", + "metadata": {}, + "outputs": [], + "source": [ + "# type\n", + "print(type(ds))\n", + "print(type(ds.variables))\n", + "print(type(ds.dims))\n", + "print(type(ds.coords))\n", + "print(type(ds.attrs))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "643399d6", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: retrieve variable names only\n", + "list(ds.variables)" + ] + }, + { + "cell_type": "markdown", + "id": "1cf36878", + "metadata": {}, + "source": [ + "#### Discussion: variable vs. coordinates vs. dimenssions. \n", + "From [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#):\n", + "\n", + "* Dimession: A dimension may be used to represent a real physical dimension, for example, time, latitude, longitude, or height. A dimension might also be used to index other quantities, for example station or model-run-number.\n", + "A netCDF dimension has both a name and a length. A dimension length is an arbitrary positive integer, except that one dimension in a netCDF dataset can have the length UNLIMITED.\n", + "\n", + "* Variables: Variables are used to store the bulk of the data in a netCDF dataset. A variable represents an array of values of the same type. A scalar value is treated as a 0-dimensional array. A variable has a name, a data type, and a shape described by its list of dimensions specified when the variable is created. A variable may also have associated attributes, which may be added, deleted or changed after the variable is created.\n", + "\n", + "* Coordinate (Variables): It is legal for a variable to have the same name as a dimension. Such variables have no special meaning to the netCDF library. However there is a convention that such variables should be treated in a special way by software using this library.\n", + "A variable with the same name as a dimension is called a coordinate variable. It typically defines a physical coordinate corresponding to that dimension.\n", + "\n", + "\n", + "Tips: By definition, coordinates and dimenssions are also variable. For example ds.time is a coordinate but it is also a special variable. But in practice, when we talk about variable, it implies regular/non-coordinate variable. We will use this convention for the remaining notebook. " + ] + }, + { + "cell_type": "markdown", + "id": "d47120f9", + "metadata": {}, + "source": [ + "### Xarray Variable\n", + "\n", + "\n", + "We can use `ds.variables` to access varialbes of a dataset. But many times viewing print-out the whole datasets can be overwhemling. Instead, we would work on the individual variable (also called Xarray data array.) using the `ds[var_name]` syntax, where `var_name` is the name of the variable. See the following examples in action.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5a57e136", + "metadata": {}, + "outputs": [], + "source": [ + "print(type(ds[\"time\"]))\n", + "ds[\"time\"]" + ] + }, + { + "cell_type": "markdown", + "id": "51ec643e", + "metadata": {}, + "source": [ + "#### Variable properties\n", + "For individual varible, the following properties are mostly used with the assocaited xarray method to retrieve them. (assuming var = ds[var_name])\n", + "* name: `var.name`\n", + "* data content: `var.data`\n", + "* attributes: `var.attrs`\n", + "* dimenstions: `var.dims`\n", + "* data type: `var.data.dtype`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a632a525", + "metadata": {}, + "outputs": [], + "source": [ + "var_name = \"time\"\n", + "var = ds[var_name]\n", + "\n", + "print(\"var.name: \\n\", var.name, \"\\n\")\n", + "print(\"var.data: \\n\", var.data, \"\\n\")\n", + "print(\"var.attrs: \\n\", var.attrs, \"\\n\")\n", + "print(\"var.dims: \\n\", var.dims, \"\\n\")\n", + "print(\"var.data.dtype: \\n\", var.data.dtype, \"\\n\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b256b07f", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: indivdual variable info table (collect and display variable info as a tabular format)\n", + "df_info = pd.DataFrame()\n", + "df_info[\"var_name\"] = list(ds.variables)\n", + "df_info[\"dims\"] = df_info.var_name.apply(lambda x: ds[x].dims)\n", + "df_info[\"is_dim\"] = df_info.var_name.apply(lambda x: x in ds.dims)\n", + "df_info[\"n_dim\"] = df_info.var_name.apply(lambda x: len(ds[x].dims))\n", + "df_info[\"attrs\"] = df_info.var_name.apply(lambda x: ds[x].attrs)\n", + "df_info[\"dtype\"] = df_info.var_name.apply(lambda x: ds[x].data.dtype)\n", + "df_info" + ] + }, + { + "cell_type": "markdown", + "id": "e6a36b96", + "metadata": {}, + "source": [ + "### Data cleaning/Preprocessing (skipped)\n", + "Data cleaning and preprocessing is an important stage in the the data analysis pipeline. However it will be out of the scope of this notebook. For more information about data cleaning and preprocessing basics, here are some references. \n", + "* [Xarray Fundamentals](https://earth-env-data-science.github.io/lectures/xarray/xarray.html)\n", + "* [Xarray Tutorial — Pangeo Gallery documentation](http://gallery.pangeo.io/repos/pangeo-data/pangeo-tutorial-gallery/xarray.html)\n", + "* [Pythonic Data Cleaning With pandas and NumPy](https://realpython.com/python-data-cleaning-numpy-pandas/)\n", + "* [Pandas - Cleaning Data](https://www.w3schools.com/python/pandas/pandas_cleaning.asp)" + ] + }, + { + "cell_type": "markdown", + "id": "ae6e1a69", + "metadata": {}, + "source": [ + "### Plotting\n", + "Data visualization (or plotting) is another important data analysis topic and deserves its own discussion. Here in this notebook we will only demonstrate basic tool for simple data visualization tasks.\n", + "\n", + "Here are some reference you might find useful:\n", + "* xarray plotting: https://docs.xarray.dev/en/stable/user-guide/plotting.html\n", + "* Atmospheric Community Toolkit (ACT): https://arm-doe.github.io/ACT/index.html\n", + "\n", + "Note: this notebook is auto-generated using a template. It uses a general idea to select variable(s) to plot and is not customized to each indivdual notebook. Feel free to change the variables in intrrests, especially certain figure is failed to plot." + ] + }, + { + "cell_type": "markdown", + "id": "7e417fb2", + "metadata": {}, + "source": [ + "#### 1-dimenssional basic time series plot\n", + "\n", + "For the following plot we would like to find variables such that\n", + "* it has one and only one dimession\n", + "* \"time\" is its coordinate variable\n", + "* it is not a dimenssion itself,\n", + "* it is not a special variable with substrings within [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "\n", + "Please see the following example in action. (For more details about pandas filtering, please see the following references.)\n", + "* [pandas: multiple conditions while indexing data frame](https://stackoverflow.com/questions/22591174/pandas-multiple-conditions-while-indexing-data-frame-unexpected-behavior)\n", + "* [pandas.DataFrame.apply](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.apply.html)\n", + "* [How to test if a string contains one of the substrings in a list...](https://stackoverflow.com/questions/26577516/how-to-test-if-a-string-contains-one-of-the-substrings-in-a-list-in-pandas" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30edac2d", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==1, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter = df_info[(df_info.n_dim == 1) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4777a659", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_1d = df_filter.var_name.values[0]\n", + " var_1d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb5ae985", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " ds[var_1d].plot()\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "9e41066e", + "metadata": {}, + "source": [ + "#### 2-dimenssional basic plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09bd4adc", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==2, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_2 = df_info[(df_info.n_dim == 2) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2cdea1a9", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_2d = df_filter_2.var_name.values[0]\n", + " var_2d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5f48879", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " print(ds[var_2d].dims)\n", + " # ds[var_2d].plot()\n", + "\n", + " # conventionally, use \"time\" as x-axis\n", + " ds[var_2d].plot(x=\"time\")\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "7554b2a6", + "metadata": {}, + "source": [ + "#### qc-plotting (optional)\n", + "\n", + "Note: act qc plotting has more strict requirement, one of them is the associated qc variable need to have \"flag_masks\" attributes. Which is added by using ds = act.io.armfiles.read_netcdf(files_list), then ds.clean.cleanup()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71c2096f", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " ds_act = act.io.armfiles.read_netcdf(full_path)\n", + " print(type(ds_act))\n", + " ds_act.clean.cleanup()\n", + "\n", + " # or \n", + " # ds.clean.cleanup()\n", + "except Exception as e:\n", + " print(\"ERROR\", e)\n", + " ds_act = ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "844f8505", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter valid variables for ACT qc plotting\n", + "condition = (\"qc_\" + df_info.var_name).apply(lambda x: ds_act[x].attrs.get(\"flag_masks\") is not None \n", + " if x in list(ds.variables) else False)\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_3 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings))) &\n", + " condition\n", + " ]\n", + "df_filter_3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd38cc6", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "try:\n", + " qc_variable = df_filter_3.var_name.values[0]\n", + " print(qc_variable)\n", + "except Exception as e:\n", + " print(e)\n", + " \n", + "try:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + " qc_ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "d76e4d27", + "metadata": {}, + "source": [ + "#### bonus: choose variables to plot from a dropdown menu " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb733804", + "metadata": {}, + "outputs": [], + "source": [ + "# Valid variables filtering\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_4 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82d5c20d", + "metadata": {}, + "outputs": [], + "source": [ + "# example 1: using xarray plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2fc9aaa4", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# fig, ax = plt.subplots(figsize=(10, 4))\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "# fig.clear() # Remove old lines from plot and plot new one\n", + "# if len(ds[var].dims)==2:\n", + "# ds[var].plot(x=\"time\", add_colorbar=False)\n", + "# else:\n", + "# ds[var].plot()\n", + "# plt.grid()\n", + "# plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fabdd802", + "metadata": {}, + "outputs": [], + "source": [ + "# example 2: using act plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "962b4186", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "\n", + "\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "\n", + "# i_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + "# i_display.add_subplots((1,), figsize=(10, 4))\n", + "# ax = i_display.plot(var, subplot_index=(0,), set_title=f\"{var} ({ds_act[var].attrs['long_name']})\",)\n", + "\n", + "# ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "# ax.grid()\n", + "# plt.show()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/AIP/aip1ogren.c1.ipynb b/VAPs/quicklook/AIP/aip1ogren.c1.ipynb new file mode 100644 index 00000000..7eebfb91 --- /dev/null +++ b/VAPs/quicklook/AIP/aip1ogren.c1.ipynb @@ -0,0 +1,339 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# AIP1OGREN.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/aip) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'aip1ogren'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2008-12-27', 'facility': 'M1', 'site': 'hfe', 'start_date': '2008-05-09'}, {'end_date': '2007-12-30', 'facility': 'M1', 'site': 'fkb', 'start_date': '2007-03-19'}, {'end_date': '2015-11-30', 'facility': 'M1', 'site': 'mao', 'start_date': '2014-01-01'}, {'end_date': '2010-12-30', 'facility': 'M1', 'site': 'grw', 'start_date': '2009-04-14'}, {'end_date': '2017-03-30', 'facility': 'C1', 'site': 'sgp', 'start_date': '1996-07-02'}, {'end_date': '2005-09-14', 'facility': 'M1', 'site': 'pye', 'start_date': '2005-03-09'}, {'end_date': '2006-12-30', 'facility': 'M1', 'site': 'nim', 'start_date': '2005-11-19'}, {'end_date': '2012-03-26', 'facility': 'M1', 'site': 'pgh', 'start_date': '2011-06-10'}, {'end_date': '2013-06-23', 'facility': 'M1', 'site': 'pvc', 'start_date': '2012-07-16'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2017-03-28'\n", + "date_end = '2017-03-30'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['Ba_G_Dry_10um_PSAP1W_1', 'Ba_G_Dry_1um_PSAP1W_1', 'Ba_R_Dry_10um_PSAP3W_1']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'Ba_G_Dry_10um_PSAP1W_1'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'Ba_G_Dry_10um_PSAP1W_1'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/AIP/aipavg1ogren.c1.ipynb b/VAPs/quicklook/AIP/aipavg1ogren.c1.ipynb new file mode 100644 index 00000000..8796fa97 --- /dev/null +++ b/VAPs/quicklook/AIP/aipavg1ogren.c1.ipynb @@ -0,0 +1,339 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# AIPAVG1OGREN.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/aip) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'aipavg1ogren'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2008-12-27', 'facility': 'M1', 'site': 'hfe', 'start_date': '2008-05-09'}, {'end_date': '2007-12-30', 'facility': 'M1', 'site': 'fkb', 'start_date': '2007-03-19'}, {'end_date': '2015-11-30', 'facility': 'M1', 'site': 'mao', 'start_date': '2014-01-01'}, {'end_date': '2010-12-30', 'facility': 'M1', 'site': 'grw', 'start_date': '2009-04-14'}, {'end_date': '2017-03-30', 'facility': 'C1', 'site': 'sgp', 'start_date': '1996-07-02'}, {'end_date': '2005-09-14', 'facility': 'M1', 'site': 'pye', 'start_date': '2005-03-09'}, {'end_date': '2006-12-30', 'facility': 'M1', 'site': 'nim', 'start_date': '2005-11-19'}, {'end_date': '2012-03-26', 'facility': 'M1', 'site': 'pgh', 'start_date': '2011-06-10'}, {'end_date': '2013-06-23', 'facility': 'M1', 'site': 'pvc', 'start_date': '2012-07-16'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2017-03-28'\n", + "date_end = '2017-03-30'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['Ba_G_Dry_1um_PSAP1W_1', 'Ba_G_Dry_10um_PSAP1W_1', 'Ba_R_Dry_10um_PSAP3W_1']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'Ba_G_Dry_1um_PSAP1W_1'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'Ba_G_Dry_1um_PSAP1W_1'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/AIP/aipfitrh1ogren.c1.ipynb b/VAPs/quicklook/AIP/aipfitrh1ogren.c1.ipynb new file mode 100644 index 00000000..cb8ac9cf --- /dev/null +++ b/VAPs/quicklook/AIP/aipfitrh1ogren.c1.ipynb @@ -0,0 +1,339 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# AIPFITRH1OGREN.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/aip) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'aipfitrh1ogren'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2008-12-26', 'facility': 'M1', 'site': 'hfe', 'start_date': '2008-05-09'}, {'end_date': '2007-12-09', 'facility': 'M1', 'site': 'fkb', 'start_date': '2007-03-27'}, {'end_date': '2015-05-01', 'facility': 'M1', 'site': 'mao', 'start_date': '2014-08-20'}, {'end_date': '2010-10-24', 'facility': 'M1', 'site': 'grw', 'start_date': '2009-04-16'}, {'end_date': '2016-09-29', 'facility': 'C1', 'site': 'sgp', 'start_date': '1998-12-19'}, {'end_date': '2005-09-13', 'facility': 'M1', 'site': 'pye', 'start_date': '2005-07-01'}, {'end_date': '2006-12-29', 'facility': 'M1', 'site': 'nim', 'start_date': '2005-12-04'}, {'end_date': '2011-12-31', 'facility': 'M1', 'site': 'pgh', 'start_date': '2011-06-10'}, {'end_date': '2013-06-22', 'facility': 'M1', 'site': 'pvc', 'start_date': '2012-09-29'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2016-09-27'\n", + "date_end = '2016-09-29'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['ratio_85by40_Bs_R_10um_3p', 'fRH_Bs_R_10um_3p', 'ratio_85by40_Bs_R_10um_2p']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'ratio_85by40_Bs_R_10um_3p'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'ratio_85by40_Bs_R_10um_3p'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/AOD-MFRSR/AOD-MFRSR_tutorial.ipynb b/VAPs/quicklook/AOD-MFRSR/AOD-MFRSR_tutorial.ipynb new file mode 100644 index 00000000..6b61ddb7 --- /dev/null +++ b/VAPs/quicklook/AOD-MFRSR/AOD-MFRSR_tutorial.ipynb @@ -0,0 +1,838 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# MFRSR7NCHAOD1MICH.C1 Notebook\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/aod-mfrsr) for more information about this vap." + ] + }, + { + "cell_type": "markdown", + "id": "97097763", + "metadata": {}, + "source": [ + "In this notebook, we demonstrate the workflow to explore ARM vap data (using mfrsr7nchaod1mich as an example.) Value-added products (VAPs) are higher-order data products that have been analyzed and processed to ease scientists’ use of ARM data in atmospheric research and global climate models. \n", + "Here is the main content we will cover." + ] + }, + { + "cell_type": "markdown", + "id": "eddec40f", + "metadata": {}, + "source": [ + "# Table of Content\n", + "## Access the data\n", + "* How to retrieve the data\n", + "* Data path and file name conventions\n", + "* Load data\n", + "## Explore the data\n", + "* NetCDF Data structure\n", + "* Xarray essentials\n", + "* Xarray Variable\n", + "## Plot the data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "import random\n", + "\n", + "import glob\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "2372d5be", + "metadata": {}, + "source": [ + "## Access the data" + ] + }, + { + "cell_type": "markdown", + "id": "5b0d9684", + "metadata": {}, + "source": [ + "### How to retrieve the data\n", + "We assume the path \"/data/archive\" is available where you are running this notebook. You can use `os.path.exists(\"/data/archive\")` to verify if the path exists at your machine.\n", + "\n", + "\n", + "### Data path and file name conventions\n", + "There several common terminologies regarding ARM data, for example, data-stream-name, data-level, site, facility, instrument, etc. (For more details, please see [ARM Data File Standards Version 1.2](https://www.google.com/search?q=arm+datastream+facility+cite+definition&rlz=1C1GCEJ_enUS1029US1029&ei=hb41ZICrBPukqtsPvMODIA&ved=0ahUKEwjAgeiS0aL-AhV7kmoFHbzhAAQQ4dUDCBA&uact=5&oq=arm+datastream+facility+cite+definition&gs_lcp=Cgxnd3Mtd2l6LXNlcnAQAzIFCAAQogQyBQgAEKIEMgUIABCiBDIFCAAQogQyBQgAEKIEOgoIABBHENYEELADOgoIIRCgARDDBBAKSgQIQRgAUJ4DWIoOYO0PaAFwAXgAgAGMAYgBuQmSAQMyLjmYAQCgAQHIAQjAAQE&sclient=gws-wiz-serp).) \n", + "\n", + "For example, this notebook is called `mfrsr7nchaod1mich.c1`, where `mfrsr7nchaod1mich` is the \"datastream name\", and the `{process.ds_class_level}}` is the \"data level\".\n", + "\n", + "This datastream also contains site `oli` and facility `M1`. (Note: individual datastream might have multiple site-facility pairs.)\n", + "In such a case, the data of this data-stream is stored at `/data/archive/oli/olimfrsr7nchaod1michM1.c1`, which is in the format of `//.`. We can use the following method to assign the data-stream directory `datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )`\n", + "\n", + "The data files under datastream_dir also follows naming conventions. But once reach the datastream_dir level, the most import file naming convention to differentiate the files is \"yyyyMMdd.hhmmss\", which comes handy to filter out files based on datetime. For example, we can use `glob.glob(f'{datastream_dir}/*.200709*.*')` to filter files in 2007 September.\n", + "\n", + "(Note: refer to the https://adc.arm.gov/discovery/#/ and https://adc.arm.gov/solr8/metadata/select API to explore ARM datastream and assoicated available sites and facilities\n", + "\n", + "Please see the following examples in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d7e9eb85", + "metadata": {}, + "outputs": [], + "source": [ + "# Verify if DATA_DIR path exists\n", + "DATA_DIR = \"/data/archive\"\n", + "os.path.exists(DATA_DIR)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "586993fd", + "metadata": {}, + "outputs": [], + "source": [ + "# Speicify datastream_dir following the path conventions and check its existence\n", + "DATASTREAM_NAME = \"mfrsr7nchaod1mich\"\n", + "DATA_LEVEL = \"c1\"\n", + "site = \"oli\"\n", + "facility = \"M1\"\n", + "datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )\n", + "print(datastream_dir)\n", + "print(os.path.exists(datastream_dir))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0742f7c1", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: list 5 (random) files under datastream_dir\n", + "files = os.listdir(datastream_dir)\n", + "files[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39b98a36", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: get most recent file\n", + "list_of_files = glob.glob(f\"{datastream_dir}/*\") # * means all if need specific format then *.csv\n", + "latest_file = max(list_of_files, key=os.path.getctime)\n", + "latest_file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "902d514e", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus sort datastream files based on datetime\n", + "files = os.listdir(datastream_dir)\n", + "file_sorted = files.copy()\n", + "file_sorted.sort() \n", + "print(file_sorted[:5])\n", + "\n", + "# to reverse\n", + "file_sorted_reverse = files.copy() \n", + "file_sorted_reverse.sort(reverse=True)\n", + "print(file_sorted_reverse[:5])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec5923b2", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: pattern matching\n", + "# filter the 200709** files under datastream_dir\n", + "files_filter = glob.glob(f'{datastream_dir}/*.200709*.*')\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c4a4aa18", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: use armnotebook_utils.file_filter (TODO) to filter files based on datastream info \n" + ] + }, + { + "cell_type": "markdown", + "id": "cfeb9efc", + "metadata": {}, + "source": [ + "### Load data\n", + "The arm data is stored in [NetCDF](https://en.wikipedia.org/wiki/NetCDF#:~:text=%22NetCDF%20(network%20Common%20Data%20Form,format%20for%20representing%20scientific%20data.) format. We can use xarray's `open_dataset` method to load single file, or `open_mfdataset` to open multiple files. (Note: the latter will still return a single xarray dataset object by combining multiple files.)\n", + "\n", + "Note: open_dataset keeps the file handle open and lazy loads its contents. All parameters are passed directly to open_dataset. It is a preferable method over load_dataset for memory-effiency. (more details, please see [xarray.open_dataset](https://docs.xarray.dev/en/stable/generated/xarray.open_dataset.html))\n", + "\n", + "See the following example in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a440a329", + "metadata": {}, + "outputs": [], + "source": [ + "# open single file\n", + "full_path = latest_file\n", + "print(full_path)\n", + "ds_single = xr.open_dataset(full_path)\n", + "ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0143a3d", + "metadata": {}, + "outputs": [], + "source": [ + "print(ds_single)\n", + "print(type(ds_single))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c0f8939", + "metadata": {}, + "outputs": [], + "source": [ + "# open multiple files\n", + "n_files = 3\n", + "full_paths = [os.path.join(datastream_dir, f_path) for f_path in file_sorted_reverse[:n_files]]\n", + "print(full_paths)\n", + "try: # Note: sometimes multiple files cannot be merged, so we used try except here.\n", + " ds_mutiple = xr.open_mfdataset(full_paths)\n", + " # ds_mutiple\n", + " print(type(ds_mutiple))\n", + " print(ds_mutiple)\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "3fa59943", + "metadata": {}, + "source": [ + "## Explore the data" + ] + }, + { + "cell_type": "markdown", + "id": "571f69b9", + "metadata": {}, + "source": [ + "### NetCDF Data structure \n", + "\n", + "\n", + "(If you are confident with NetCDF basics and xarray essentials, feel free to skip this session.)\n", + "Before we dive into data exploration, there are some eseential concpets we should be familiar with (shown below): \n", + "* Dataset\n", + "* Data array\n", + "* Variable\n", + "* Dimenssion\n", + "* Coordinate\n", + "* Data Type\n", + "* Meta Data (Attributes)\n", + "\n", + "We will not go into details about NetCDF basics and here are some references you might find helpful\n", + "* [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#)\n", + "* [Network Common Data Form (NetCDF)](https://www.unidata.ucar.edu/software/netcdf/) and [A Brief History of (netCDF) Time](https://www.unidata.ucar.edu/software/netcdf/time/recs.html)\n", + "\n", + "\n", + "\n", + "### Xarray essentials\n", + "Earlier we introduced the xarray pacakge and used `open_dataset` and `open_mfdataset` to retrieve NetCDF data file as an xarray Dataset object. Recall that you can review the data in a notebook by using `print(ds)` or `ds`. \n", + "\n", + "Using xarray to retrieve the aforementioned NetCDF basics is straightforwared. In general,\n", + "* Dataset: `ds`\n", + "* Data array: `ds.variables`\n", + "* Variable: `ds.ds.variables`\n", + "* Dimenssion: `ds.dims`\n", + "* Coordinate: `ds.coords`\n", + "* Data Type: `type`\n", + "* Meta Data (Attributes)\n", + "\n", + "Also, here are some references if you are new to xarray\n", + "* [Xarray in 45 minutes](https://tutorial.xarray.dev/overview/xarray-in-45-min.html)\n", + "* [Handling NetCDF Files using XArray for Absolute Beginners](https://towardsdatascience.com/handling-netcdf-files-using-xarray-for-absolute-beginners-111a8ab4463f)\n", + "\n", + "Try the following commands in action\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b3eac323", + "metadata": {}, + "outputs": [], + "source": [ + "ds = ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77ecf85d", + "metadata": {}, + "outputs": [], + "source": [ + "# Dataset \n", + "ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25e7de09", + "metadata": {}, + "outputs": [], + "source": [ + "# Data array, Variable\n", + "# Note: the info can be overwhelming. Do not attempt to grasp everything at the first glance.\n", + "ds.variables" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c41b67e", + "metadata": {}, + "outputs": [], + "source": [ + "# dimenssions\n", + "ds.dims" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "156f1dfc", + "metadata": {}, + "outputs": [], + "source": [ + "# coordinates\n", + "ds.coords" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "277d6064", + "metadata": {}, + "outputs": [], + "source": [ + "# Meta Data (Attributes)\n", + "ds.attrs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d334681f", + "metadata": {}, + "outputs": [], + "source": [ + "# type\n", + "print(type(ds))\n", + "print(type(ds.variables))\n", + "print(type(ds.dims))\n", + "print(type(ds.coords))\n", + "print(type(ds.attrs))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "643399d6", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: retrieve variable names only\n", + "list(ds.variables)" + ] + }, + { + "cell_type": "markdown", + "id": "1cf36878", + "metadata": {}, + "source": [ + "#### Discussion: variable vs. coordinates vs. dimenssions. \n", + "From [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#):\n", + "\n", + "* Dimession: A dimension may be used to represent a real physical dimension, for example, time, latitude, longitude, or height. A dimension might also be used to index other quantities, for example station or model-run-number.\n", + "A netCDF dimension has both a name and a length. A dimension length is an arbitrary positive integer, except that one dimension in a netCDF dataset can have the length UNLIMITED.\n", + "\n", + "* Variables: Variables are used to store the bulk of the data in a netCDF dataset. A variable represents an array of values of the same type. A scalar value is treated as a 0-dimensional array. A variable has a name, a data type, and a shape described by its list of dimensions specified when the variable is created. A variable may also have associated attributes, which may be added, deleted or changed after the variable is created.\n", + "\n", + "* Coordinate (Variables): It is legal for a variable to have the same name as a dimension. Such variables have no special meaning to the netCDF library. However there is a convention that such variables should be treated in a special way by software using this library.\n", + "A variable with the same name as a dimension is called a coordinate variable. It typically defines a physical coordinate corresponding to that dimension.\n", + "\n", + "\n", + "Tips: By definition, coordinates and dimenssions are also variable. For example ds.time is a coordinate but it is also a special variable. But in practice, when we talk about variable, it implies regular/non-coordinate variable. We will use this convention for the remaining notebook. " + ] + }, + { + "cell_type": "markdown", + "id": "d47120f9", + "metadata": {}, + "source": [ + "### Xarray Variable\n", + "\n", + "\n", + "We can use `ds.variables` to access varialbes of a dataset. But many times viewing print-out the whole datasets can be overwhemling. Instead, we would work on the individual variable (also called Xarray data array.) using the `ds[var_name]` syntax, where `var_name` is the name of the variable. See the following examples in action.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5a57e136", + "metadata": {}, + "outputs": [], + "source": [ + "print(type(ds[\"time\"]))\n", + "ds[\"time\"]" + ] + }, + { + "cell_type": "markdown", + "id": "51ec643e", + "metadata": {}, + "source": [ + "#### Variable properties\n", + "For individual varible, the following properties are mostly used with the assocaited xarray method to retrieve them. (assuming var = ds[var_name])\n", + "* name: `var.name`\n", + "* data content: `var.data`\n", + "* attributes: `var.attrs`\n", + "* dimenstions: `var.dims`\n", + "* data type: `var.data.dtype`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a632a525", + "metadata": {}, + "outputs": [], + "source": [ + "var_name = \"time\"\n", + "var = ds[var_name]\n", + "\n", + "print(\"var.name: \\n\", var.name, \"\\n\")\n", + "print(\"var.data: \\n\", var.data, \"\\n\")\n", + "print(\"var.attrs: \\n\", var.attrs, \"\\n\")\n", + "print(\"var.dims: \\n\", var.dims, \"\\n\")\n", + "print(\"var.data.dtype: \\n\", var.data.dtype, \"\\n\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b256b07f", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: indivdual variable info table (collect and display variable info as a tabular format)\n", + "df_info = pd.DataFrame()\n", + "df_info[\"var_name\"] = list(ds.variables)\n", + "df_info[\"dims\"] = df_info.var_name.apply(lambda x: ds[x].dims)\n", + "df_info[\"is_dim\"] = df_info.var_name.apply(lambda x: x in ds.dims)\n", + "df_info[\"n_dim\"] = df_info.var_name.apply(lambda x: len(ds[x].dims))\n", + "df_info[\"attrs\"] = df_info.var_name.apply(lambda x: ds[x].attrs)\n", + "df_info[\"dtype\"] = df_info.var_name.apply(lambda x: ds[x].data.dtype)\n", + "df_info" + ] + }, + { + "cell_type": "markdown", + "id": "e6a36b96", + "metadata": {}, + "source": [ + "### Data cleaning/Preprocessing (skipped)\n", + "Data cleaning and preprocessing is an important stage in the the data analysis pipeline. However it will be out of the scope of this notebook. For more information about data cleaning and preprocessing basics, here are some references. \n", + "* [Xarray Fundamentals](https://earth-env-data-science.github.io/lectures/xarray/xarray.html)\n", + "* [Xarray Tutorial — Pangeo Gallery documentation](http://gallery.pangeo.io/repos/pangeo-data/pangeo-tutorial-gallery/xarray.html)\n", + "* [Pythonic Data Cleaning With pandas and NumPy](https://realpython.com/python-data-cleaning-numpy-pandas/)\n", + "* [Pandas - Cleaning Data](https://www.w3schools.com/python/pandas/pandas_cleaning.asp)" + ] + }, + { + "cell_type": "markdown", + "id": "ae6e1a69", + "metadata": {}, + "source": [ + "### Plotting\n", + "Data visualization (or plotting) is another important data analysis topic and deserves its own discussion. Here in this notebook we will only demonstrate basic tool for simple data visualization tasks.\n", + "\n", + "Here are some reference you might find useful:\n", + "* xarray plotting: https://docs.xarray.dev/en/stable/user-guide/plotting.html\n", + "* Atmospheric Community Toolkit (ACT): https://arm-doe.github.io/ACT/index.html\n", + "\n", + "Note: this notebook is auto-generated using a template. It uses a general idea to select variable(s) to plot and is not customized to each indivdual notebook. Feel free to change the variables in intrrests, especially certain figure is failed to plot." + ] + }, + { + "cell_type": "markdown", + "id": "7e417fb2", + "metadata": {}, + "source": [ + "#### 1-dimenssional basic time series plot\n", + "\n", + "For the following plot we would like to find variables such that\n", + "* it has one and only one dimession\n", + "* \"time\" is its coordinate variable\n", + "* it is not a dimenssion itself,\n", + "* it is not a special variable with substrings within [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "\n", + "Please see the following example in action. (For more details about pandas filtering, please see the following references.)\n", + "* [pandas: multiple conditions while indexing data frame](https://stackoverflow.com/questions/22591174/pandas-multiple-conditions-while-indexing-data-frame-unexpected-behavior)\n", + "* [pandas.DataFrame.apply](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.apply.html)\n", + "* [How to test if a string contains one of the substrings in a list...](https://stackoverflow.com/questions/26577516/how-to-test-if-a-string-contains-one-of-the-substrings-in-a-list-in-pandas" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30edac2d", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==1, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter = df_info[(df_info.n_dim == 1) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4777a659", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_1d = df_filter.var_name.values[0]\n", + " var_1d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb5ae985", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " ds[var_1d].plot()\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "9e41066e", + "metadata": {}, + "source": [ + "#### 2-dimenssional basic plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09bd4adc", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==2, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_2 = df_info[(df_info.n_dim == 2) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2cdea1a9", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_2d = df_filter_2.var_name.values[0]\n", + " var_2d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5f48879", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " print(ds[var_2d].dims)\n", + " # ds[var_2d].plot()\n", + "\n", + " # conventionally, use \"time\" as x-axis\n", + " ds[var_2d].plot(x=\"time\")\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "7554b2a6", + "metadata": {}, + "source": [ + "#### qc-plotting (optional)\n", + "\n", + "Note: act qc plotting has more strict requirement, one of them is the associated qc variable need to have \"flag_masks\" attributes. Which is added by using ds = act.io.armfiles.read_netcdf(files_list), then ds.clean.cleanup()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71c2096f", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " ds_act = act.io.armfiles.read_netcdf(full_path)\n", + " print(type(ds_act))\n", + " ds_act.clean.cleanup()\n", + "\n", + " # or \n", + " # ds.clean.cleanup()\n", + "except Exception as e:\n", + " print(\"ERROR\", e)\n", + " ds_act = ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "844f8505", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter valid variables for ACT qc plotting\n", + "condition = (\"qc_\" + df_info.var_name).apply(lambda x: ds_act[x].attrs.get(\"flag_masks\") is not None \n", + " if x in list(ds.variables) else False)\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_3 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings))) &\n", + " condition\n", + " ]\n", + "df_filter_3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd38cc6", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "try:\n", + " qc_variable = df_filter_3.var_name.values[0]\n", + " print(qc_variable)\n", + "except Exception as e:\n", + " print(e)\n", + " \n", + "try:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + " qc_ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "d76e4d27", + "metadata": {}, + "source": [ + "#### bonus: choose variables to plot from a dropdown menu " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb733804", + "metadata": {}, + "outputs": [], + "source": [ + "# Valid variables filtering\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_4 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82d5c20d", + "metadata": {}, + "outputs": [], + "source": [ + "# example 1: using xarray plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2fc9aaa4", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# fig, ax = plt.subplots(figsize=(10, 4))\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "# fig.clear() # Remove old lines from plot and plot new one\n", + "# if len(ds[var].dims)==2:\n", + "# ds[var].plot(x=\"time\", add_colorbar=False)\n", + "# else:\n", + "# ds[var].plot()\n", + "# plt.grid()\n", + "# plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fabdd802", + "metadata": {}, + "outputs": [], + "source": [ + "# example 2: using act plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "962b4186", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "\n", + "\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "\n", + "# i_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + "# i_display.add_subplots((1,), figsize=(10, 4))\n", + "# ax = i_display.plot(var, subplot_index=(0,), set_title=f\"{var} ({ds_act[var].attrs['long_name']})\",)\n", + "\n", + "# ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "# ax.grid()\n", + "# plt.show()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/AOD-MFRSR/mfrsr7nchaod1mich.c1.ipynb b/VAPs/quicklook/AOD-MFRSR/mfrsr7nchaod1mich.c1.ipynb new file mode 100644 index 00000000..a7546b36 --- /dev/null +++ b/VAPs/quicklook/AOD-MFRSR/mfrsr7nchaod1mich.c1.ipynb @@ -0,0 +1,339 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# MFRSR7NCHAOD1MICH.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/aod-mfrsr) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'mfrsr7nchaod1mich'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2021-06-14', 'facility': 'M1', 'site': 'oli', 'start_date': '2021-04-03'}, {'end_date': '2022-10-02', 'facility': 'M1', 'site': 'hou', 'start_date': '2021-08-11'}, {'end_date': '2022-10-19', 'facility': 'C1', 'site': 'sgp', 'start_date': '2021-05-10'}, {'end_date': '2022-09-26', 'facility': 'E11', 'site': 'sgp', 'start_date': '2020-12-16'}, {'end_date': '2022-09-26', 'facility': 'E12', 'site': 'sgp', 'start_date': '2020-12-14'}, {'end_date': '2023-12-05', 'facility': 'E13', 'site': 'sgp', 'start_date': '2021-01-12'}, {'end_date': '2022-09-26', 'facility': 'E15', 'site': 'sgp', 'start_date': '2020-12-04'}, {'end_date': '2021-09-21', 'facility': 'E31', 'site': 'sgp', 'start_date': '2020-12-10'}, {'end_date': '2022-09-26', 'facility': 'E32', 'site': 'sgp', 'start_date': '2020-11-19'}, {'end_date': '2022-09-26', 'facility': 'E33', 'site': 'sgp', 'start_date': '2020-11-25'}, {'end_date': '2022-09-26', 'facility': 'E34', 'site': 'sgp', 'start_date': '2020-12-17'}, {'end_date': '2022-09-26', 'facility': 'E35', 'site': 'sgp', 'start_date': '2020-12-19'}, {'end_date': '2022-09-26', 'facility': 'E36', 'site': 'sgp', 'start_date': '2020-12-08'}, {'end_date': '2022-09-26', 'facility': 'E37', 'site': 'sgp', 'start_date': '2020-11-30'}, {'end_date': '2021-06-07', 'facility': 'E38', 'site': 'sgp', 'start_date': '2017-11-15'}, {'end_date': '2022-09-26', 'facility': 'E39', 'site': 'sgp', 'start_date': '2020-12-04'}, {'end_date': '2022-09-26', 'facility': 'E40', 'site': 'sgp', 'start_date': '2020-12-17'}, {'end_date': '2021-12-09', 'facility': 'E41', 'site': 'sgp', 'start_date': '2020-12-05'}, {'end_date': '2022-09-26', 'facility': 'E9', 'site': 'sgp', 'start_date': '2021-02-13'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2022-10-19'\n", + "date_end = '2022-10-19'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['hemisp_narrowband_filter1', 'hemisp_narrowband_filter2', 'hemisp_narrowband_filter3']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'hemisp_narrowband_filter1'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'hemisp_narrowband_filter1'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/AOD-MFRSR/mfrsr7nchcal.c1.ipynb b/VAPs/quicklook/AOD-MFRSR/mfrsr7nchcal.c1.ipynb new file mode 100644 index 00000000..66f7fd27 --- /dev/null +++ b/VAPs/quicklook/AOD-MFRSR/mfrsr7nchcal.c1.ipynb @@ -0,0 +1,339 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# MFRSR7NCHCAL.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/aod-mfrsr) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'mfrsr7nchcal'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2021-06-14', 'facility': 'M1', 'site': 'oli', 'start_date': '2021-04-03'}, {'end_date': '2022-10-02', 'facility': 'M1', 'site': 'hou', 'start_date': '2021-08-11'}, {'end_date': '2022-10-19', 'facility': 'C1', 'site': 'sgp', 'start_date': '2021-05-10'}, {'end_date': '2022-09-26', 'facility': 'E11', 'site': 'sgp', 'start_date': '2020-12-16'}, {'end_date': '2022-09-26', 'facility': 'E12', 'site': 'sgp', 'start_date': '2020-12-14'}, {'end_date': '2023-12-05', 'facility': 'E13', 'site': 'sgp', 'start_date': '2021-01-12'}, {'end_date': '2022-09-26', 'facility': 'E15', 'site': 'sgp', 'start_date': '2020-12-04'}, {'end_date': '2021-09-21', 'facility': 'E31', 'site': 'sgp', 'start_date': '2020-12-10'}, {'end_date': '2022-09-26', 'facility': 'E32', 'site': 'sgp', 'start_date': '2020-11-19'}, {'end_date': '2022-09-26', 'facility': 'E33', 'site': 'sgp', 'start_date': '2020-11-25'}, {'end_date': '2022-09-26', 'facility': 'E34', 'site': 'sgp', 'start_date': '2020-12-17'}, {'end_date': '2022-09-26', 'facility': 'E35', 'site': 'sgp', 'start_date': '2020-12-19'}, {'end_date': '2022-09-26', 'facility': 'E36', 'site': 'sgp', 'start_date': '2020-12-08'}, {'end_date': '2022-09-26', 'facility': 'E37', 'site': 'sgp', 'start_date': '2020-11-30'}, {'end_date': '2021-06-07', 'facility': 'E38', 'site': 'sgp', 'start_date': '2017-11-15'}, {'end_date': '2022-09-26', 'facility': 'E39', 'site': 'sgp', 'start_date': '2020-12-04'}, {'end_date': '2022-09-26', 'facility': 'E40', 'site': 'sgp', 'start_date': '2020-12-17'}, {'end_date': '2021-12-09', 'facility': 'E41', 'site': 'sgp', 'start_date': '2020-12-05'}, {'end_date': '2022-09-26', 'facility': 'E9', 'site': 'sgp', 'start_date': '2021-02-13'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2022-10-19'\n", + "date_end = '2022-10-19'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['Ozone_column_amount']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'Io_filter1'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'Ozone_column_amount'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/AOD-MFRSR/mfrsraod1mich.c1.ipynb b/VAPs/quicklook/AOD-MFRSR/mfrsraod1mich.c1.ipynb new file mode 100644 index 00000000..1ab57070 --- /dev/null +++ b/VAPs/quicklook/AOD-MFRSR/mfrsraod1mich.c1.ipynb @@ -0,0 +1,339 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# MFRSRAOD1MICH.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/aod-mfrsr) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'mfrsraod1mich'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2017-08-09', 'facility': 'M1', 'site': 'asi', 'start_date': '2016-05-02'}, {'end_date': '2021-03-30', 'facility': 'M1', 'site': 'oli', 'start_date': '2014-04-20'}, {'end_date': '2019-04-30', 'facility': 'M1', 'site': 'cor', 'start_date': '2018-10-01'}, {'end_date': '2020-01-25', 'facility': 'C1', 'site': 'ena', 'start_date': '2016-01-01'}, {'end_date': '2007-12-29', 'facility': 'M1', 'site': 'fkb', 'start_date': '2007-04-19'}, {'end_date': '2015-10-24', 'facility': 'M1', 'site': 'mao', 'start_date': '2014-01-01'}, {'end_date': '2018-03-13', 'facility': 'S1', 'site': 'mcq', 'start_date': '2016-04-01'}, {'end_date': '2008-12-26', 'facility': 'M1', 'site': 'hfe', 'start_date': '2008-05-30'}, {'end_date': '2012-02-06', 'facility': 'M1', 'site': 'gan', 'start_date': '2011-09-24'}, {'end_date': '2011-01-03', 'facility': 'M1', 'site': 'grw', 'start_date': '2009-05-05'}, {'end_date': '2013-04-04', 'facility': 'M1', 'site': 'pvc', 'start_date': '2012-07-09'}, {'end_date': '2005-09-04', 'facility': 'M1', 'site': 'pye', 'start_date': '2005-02-20'}, {'end_date': '2011-04-26', 'facility': 'M1', 'site': 'sbs', 'start_date': '2011-01-26'}, {'end_date': '2012-03-27', 'facility': 'M1', 'site': 'pgh', 'start_date': '2011-06-25'}, {'end_date': '2020-08-03', 'facility': 'C1', 'site': 'nsa', 'start_date': '1998-04-02'}, {'end_date': '2010-11-13', 'facility': 'C2', 'site': 'nsa', 'start_date': '1999-09-05'}, {'end_date': '2021-04-30', 'facility': 'C1', 'site': 'sgp', 'start_date': '1997-01-16'}, {'end_date': '2011-10-19', 'facility': 'E10', 'site': 'sgp', 'start_date': '1997-10-31'}, {'end_date': '2020-12-08', 'facility': 'E11', 'site': 'sgp', 'start_date': '1997-09-01'}, {'end_date': '2020-12-08', 'facility': 'E12', 'site': 'sgp', 'start_date': '1997-11-10'}, {'end_date': '2021-01-08', 'facility': 'E13', 'site': 'sgp', 'start_date': '1997-09-10'}, {'end_date': '2020-11-30', 'facility': 'E15', 'site': 'sgp', 'start_date': '1997-09-10'}, {'end_date': '2011-11-15', 'facility': 'E16', 'site': 'sgp', 'start_date': '1997-08-21'}, {'end_date': '2009-11-17', 'facility': 'E18', 'site': 'sgp', 'start_date': '1997-10-17'}, {'end_date': '2011-04-27', 'facility': 'E19', 'site': 'sgp', 'start_date': '1998-07-27'}, {'end_date': '2009-10-14', 'facility': 'E1', 'site': 'sgp', 'start_date': '1997-12-22'}, {'end_date': '2011-11-17', 'facility': 'E20', 'site': 'sgp', 'start_date': '1997-12-26'}, {'end_date': '2009-12-01', 'facility': 'E22', 'site': 'sgp', 'start_date': '1997-11-28'}, {'end_date': '2009-11-14', 'facility': 'E24', 'site': 'sgp', 'start_date': '1997-11-28'}, {'end_date': '2002-04-08', 'facility': 'E25', 'site': 'sgp', 'start_date': '1997-10-22'}, {'end_date': '2009-12-04', 'facility': 'E27', 'site': 'sgp', 'start_date': '2004-01-22'}, {'end_date': '2009-10-20', 'facility': 'E2', 'site': 'sgp', 'start_date': '1997-11-25'}, {'end_date': '2020-12-07', 'facility': 'E31', 'site': 'sgp', 'start_date': '2011-10-13'}, {'end_date': '2020-11-17', 'facility': 'E32', 'site': 'sgp', 'start_date': '2011-12-07'}, {'end_date': '2020-11-23', 'facility': 'E33', 'site': 'sgp', 'start_date': '2011-09-27'}, {'end_date': '2020-12-08', 'facility': 'E34', 'site': 'sgp', 'start_date': '2011-09-28'}, {'end_date': '2020-12-14', 'facility': 'E35', 'site': 'sgp', 'start_date': '2011-09-28'}, {'end_date': '2020-12-03', 'facility': 'E36', 'site': 'sgp', 'start_date': '2011-10-18'}, {'end_date': '2020-11-24', 'facility': 'E37', 'site': 'sgp', 'start_date': '2011-11-03'}, {'end_date': '2017-09-29', 'facility': 'E38', 'site': 'sgp', 'start_date': '2011-11-01'}, {'end_date': '2009-10-28', 'facility': 'E3', 'site': 'sgp', 'start_date': '1997-11-11'}, {'end_date': '2020-12-14', 'facility': 'E40', 'site': 'sgp', 'start_date': '2015-11-13'}, {'end_date': '2011-09-26', 'facility': 'E4', 'site': 'sgp', 'start_date': '1997-12-18'}, {'end_date': '2009-10-14', 'facility': 'E5', 'site': 'sgp', 'start_date': '1997-11-10'}, {'end_date': '2011-09-30', 'facility': 'E6', 'site': 'sgp', 'start_date': '1997-11-10'}, {'end_date': '2011-11-14', 'facility': 'E7', 'site': 'sgp', 'start_date': '1997-11-01'}, {'end_date': '2009-11-10', 'facility': 'E8', 'site': 'sgp', 'start_date': '1997-09-04'}, {'end_date': '2020-11-16', 'facility': 'E9', 'site': 'sgp', 'start_date': '1998-03-01'}, {'end_date': '2014-09-15', 'facility': 'M1', 'site': 'tmp', 'start_date': '2014-02-01'}, {'end_date': '2014-06-25', 'facility': 'C1', 'site': 'twp', 'start_date': '1997-08-28'}, {'end_date': '2013-09-09', 'facility': 'C2', 'site': 'twp', 'start_date': '1998-11-10'}, {'end_date': '2015-01-06', 'facility': 'C3', 'site': 'twp', 'start_date': '2002-03-22'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2021-04-30'\n", + "date_end = '2021-04-30'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['hemisp_narrowband_filter1', 'hemisp_narrowband_filter2', 'hemisp_narrowband_filter3']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'hemisp_broadband'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'hemisp_narrowband_filter1'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/AOD-MFRSR/mfrsrcal.c1.ipynb b/VAPs/quicklook/AOD-MFRSR/mfrsrcal.c1.ipynb new file mode 100644 index 00000000..2e35fc3a --- /dev/null +++ b/VAPs/quicklook/AOD-MFRSR/mfrsrcal.c1.ipynb @@ -0,0 +1,339 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# MFRSRCAL.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/aod-mfrsr) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'mfrsrcal'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2017-08-09', 'facility': 'M1', 'site': 'asi', 'start_date': '2016-05-02'}, {'end_date': '2021-03-30', 'facility': 'M1', 'site': 'oli', 'start_date': '2016-09-12'}, {'end_date': '2020-01-25', 'facility': 'C1', 'site': 'ena', 'start_date': '2016-01-01'}, {'end_date': '2018-03-13', 'facility': 'S1', 'site': 'mcq', 'start_date': '2016-04-01'}, {'end_date': '2019-04-30', 'facility': 'M1', 'site': 'cor', 'start_date': '2018-10-01'}, {'end_date': '2020-08-03', 'facility': 'C1', 'site': 'nsa', 'start_date': '2017-06-18'}, {'end_date': '2021-04-30', 'facility': 'C1', 'site': 'sgp', 'start_date': '2014-09-24'}, {'end_date': '2011-10-19', 'facility': 'E10', 'site': 'sgp', 'start_date': '1997-10-31'}, {'end_date': '2020-12-08', 'facility': 'E11', 'site': 'sgp', 'start_date': '1997-09-01'}, {'end_date': '2020-12-08', 'facility': 'E12', 'site': 'sgp', 'start_date': '1997-11-10'}, {'end_date': '2021-01-08', 'facility': 'E13', 'site': 'sgp', 'start_date': '2017-12-14'}, {'end_date': '2020-11-30', 'facility': 'E15', 'site': 'sgp', 'start_date': '1997-09-10'}, {'end_date': '2011-11-15', 'facility': 'E16', 'site': 'sgp', 'start_date': '1997-08-21'}, {'end_date': '2009-11-17', 'facility': 'E18', 'site': 'sgp', 'start_date': '1997-10-17'}, {'end_date': '2009-10-14', 'facility': 'E1', 'site': 'sgp', 'start_date': '1997-12-22'}, {'end_date': '2011-11-17', 'facility': 'E20', 'site': 'sgp', 'start_date': '1997-12-26'}, {'end_date': '2009-12-01', 'facility': 'E22', 'site': 'sgp', 'start_date': '1997-11-28'}, {'end_date': '2009-11-14', 'facility': 'E24', 'site': 'sgp', 'start_date': '1997-11-28'}, {'end_date': '2002-04-08', 'facility': 'E25', 'site': 'sgp', 'start_date': '1997-10-22'}, {'end_date': '2009-12-04', 'facility': 'E27', 'site': 'sgp', 'start_date': '2004-01-22'}, {'end_date': '2009-10-20', 'facility': 'E2', 'site': 'sgp', 'start_date': '1997-11-25'}, {'end_date': '2020-12-07', 'facility': 'E31', 'site': 'sgp', 'start_date': '2011-10-13'}, {'end_date': '2020-11-17', 'facility': 'E32', 'site': 'sgp', 'start_date': '2011-12-07'}, {'end_date': '2020-11-23', 'facility': 'E33', 'site': 'sgp', 'start_date': '2011-09-27'}, {'end_date': '2020-12-08', 'facility': 'E34', 'site': 'sgp', 'start_date': '2011-09-28'}, {'end_date': '2020-12-14', 'facility': 'E35', 'site': 'sgp', 'start_date': '2011-09-28'}, {'end_date': '2020-04-21', 'facility': 'E36', 'site': 'sgp', 'start_date': '2020-04-21'}, {'end_date': '2020-11-24', 'facility': 'E37', 'site': 'sgp', 'start_date': '2011-11-03'}, {'end_date': '2017-10-30', 'facility': 'E38', 'site': 'sgp', 'start_date': '2011-11-01'}, {'end_date': '2009-10-28', 'facility': 'E3', 'site': 'sgp', 'start_date': '1997-11-11'}, {'end_date': '2020-12-14', 'facility': 'E40', 'site': 'sgp', 'start_date': '2015-11-13'}, {'end_date': '2011-09-26', 'facility': 'E4', 'site': 'sgp', 'start_date': '1997-12-18'}, {'end_date': '2009-10-14', 'facility': 'E5', 'site': 'sgp', 'start_date': '1997-11-10'}, {'end_date': '2011-09-30', 'facility': 'E6', 'site': 'sgp', 'start_date': '1997-11-10'}, {'end_date': '2011-11-14', 'facility': 'E7', 'site': 'sgp', 'start_date': '1997-11-01'}, {'end_date': '2009-11-10', 'facility': 'E8', 'site': 'sgp', 'start_date': '1997-09-04'}, {'end_date': '2020-11-16', 'facility': 'E9', 'site': 'sgp', 'start_date': '2017-12-15'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2021-04-30'\n", + "date_end = '2021-04-30'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['Ozone_column_amount']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'Io_filter1'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'Ozone_column_amount'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/AOD-NIMFR/AOD-NIMFR_tutorial.ipynb b/VAPs/quicklook/AOD-NIMFR/AOD-NIMFR_tutorial.ipynb new file mode 100644 index 00000000..562b3cc5 --- /dev/null +++ b/VAPs/quicklook/AOD-NIMFR/AOD-NIMFR_tutorial.ipynb @@ -0,0 +1,838 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# NIMFRAOD1MICH.C1 Notebook\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/aod-nimfr) for more information about this vap." + ] + }, + { + "cell_type": "markdown", + "id": "97097763", + "metadata": {}, + "source": [ + "In this notebook, we demonstrate the workflow to explore ARM vap data (using nimfraod1mich as an example.) Value-added products (VAPs) are higher-order data products that have been analyzed and processed to ease scientists’ use of ARM data in atmospheric research and global climate models. \n", + "Here is the main content we will cover." + ] + }, + { + "cell_type": "markdown", + "id": "eddec40f", + "metadata": {}, + "source": [ + "# Table of Content\n", + "## Access the data\n", + "* How to retrieve the data\n", + "* Data path and file name conventions\n", + "* Load data\n", + "## Explore the data\n", + "* NetCDF Data structure\n", + "* Xarray essentials\n", + "* Xarray Variable\n", + "## Plot the data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "import random\n", + "\n", + "import glob\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "2372d5be", + "metadata": {}, + "source": [ + "## Access the data" + ] + }, + { + "cell_type": "markdown", + "id": "5b0d9684", + "metadata": {}, + "source": [ + "### How to retrieve the data\n", + "We assume the path \"/data/archive\" is available where you are running this notebook. You can use `os.path.exists(\"/data/archive\")` to verify if the path exists at your machine.\n", + "\n", + "\n", + "### Data path and file name conventions\n", + "There several common terminologies regarding ARM data, for example, data-stream-name, data-level, site, facility, instrument, etc. (For more details, please see [ARM Data File Standards Version 1.2](https://www.google.com/search?q=arm+datastream+facility+cite+definition&rlz=1C1GCEJ_enUS1029US1029&ei=hb41ZICrBPukqtsPvMODIA&ved=0ahUKEwjAgeiS0aL-AhV7kmoFHbzhAAQQ4dUDCBA&uact=5&oq=arm+datastream+facility+cite+definition&gs_lcp=Cgxnd3Mtd2l6LXNlcnAQAzIFCAAQogQyBQgAEKIEMgUIABCiBDIFCAAQogQyBQgAEKIEOgoIABBHENYEELADOgoIIRCgARDDBBAKSgQIQRgAUJ4DWIoOYO0PaAFwAXgAgAGMAYgBuQmSAQMyLjmYAQCgAQHIAQjAAQE&sclient=gws-wiz-serp).) \n", + "\n", + "For example, this notebook is called `nimfraod1mich.c1`, where `nimfraod1mich` is the \"datastream name\", and the `{process.ds_class_level}}` is the \"data level\".\n", + "\n", + "This datastream also contains site `nsa` and facility `C1`. (Note: individual datastream might have multiple site-facility pairs.)\n", + "In such a case, the data of this data-stream is stored at `/data/archive/nsa/nsanimfraod1michC1.c1`, which is in the format of `//.`. We can use the following method to assign the data-stream directory `datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )`\n", + "\n", + "The data files under datastream_dir also follows naming conventions. But once reach the datastream_dir level, the most import file naming convention to differentiate the files is \"yyyyMMdd.hhmmss\", which comes handy to filter out files based on datetime. For example, we can use `glob.glob(f'{datastream_dir}/*.200709*.*')` to filter files in 2007 September.\n", + "\n", + "(Note: refer to the https://adc.arm.gov/discovery/#/ and https://adc.arm.gov/solr8/metadata/select API to explore ARM datastream and assoicated available sites and facilities\n", + "\n", + "Please see the following examples in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d7e9eb85", + "metadata": {}, + "outputs": [], + "source": [ + "# Verify if DATA_DIR path exists\n", + "DATA_DIR = \"/data/archive\"\n", + "os.path.exists(DATA_DIR)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "586993fd", + "metadata": {}, + "outputs": [], + "source": [ + "# Speicify datastream_dir following the path conventions and check its existence\n", + "DATASTREAM_NAME = \"nimfraod1mich\"\n", + "DATA_LEVEL = \"c1\"\n", + "site = \"nsa\"\n", + "facility = \"C1\"\n", + "datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )\n", + "print(datastream_dir)\n", + "print(os.path.exists(datastream_dir))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0742f7c1", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: list 5 (random) files under datastream_dir\n", + "files = os.listdir(datastream_dir)\n", + "files[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39b98a36", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: get most recent file\n", + "list_of_files = glob.glob(f\"{datastream_dir}/*\") # * means all if need specific format then *.csv\n", + "latest_file = max(list_of_files, key=os.path.getctime)\n", + "latest_file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "902d514e", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus sort datastream files based on datetime\n", + "files = os.listdir(datastream_dir)\n", + "file_sorted = files.copy()\n", + "file_sorted.sort() \n", + "print(file_sorted[:5])\n", + "\n", + "# to reverse\n", + "file_sorted_reverse = files.copy() \n", + "file_sorted_reverse.sort(reverse=True)\n", + "print(file_sorted_reverse[:5])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec5923b2", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: pattern matching\n", + "# filter the 200709** files under datastream_dir\n", + "files_filter = glob.glob(f'{datastream_dir}/*.200709*.*')\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c4a4aa18", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: use armnotebook_utils.file_filter (TODO) to filter files based on datastream info \n" + ] + }, + { + "cell_type": "markdown", + "id": "cfeb9efc", + "metadata": {}, + "source": [ + "### Load data\n", + "The arm data is stored in [NetCDF](https://en.wikipedia.org/wiki/NetCDF#:~:text=%22NetCDF%20(network%20Common%20Data%20Form,format%20for%20representing%20scientific%20data.) format. We can use xarray's `open_dataset` method to load single file, or `open_mfdataset` to open multiple files. (Note: the latter will still return a single xarray dataset object by combining multiple files.)\n", + "\n", + "Note: open_dataset keeps the file handle open and lazy loads its contents. All parameters are passed directly to open_dataset. It is a preferable method over load_dataset for memory-effiency. (more details, please see [xarray.open_dataset](https://docs.xarray.dev/en/stable/generated/xarray.open_dataset.html))\n", + "\n", + "See the following example in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a440a329", + "metadata": {}, + "outputs": [], + "source": [ + "# open single file\n", + "full_path = latest_file\n", + "print(full_path)\n", + "ds_single = xr.open_dataset(full_path)\n", + "ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0143a3d", + "metadata": {}, + "outputs": [], + "source": [ + "print(ds_single)\n", + "print(type(ds_single))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c0f8939", + "metadata": {}, + "outputs": [], + "source": [ + "# open multiple files\n", + "n_files = 3\n", + "full_paths = [os.path.join(datastream_dir, f_path) for f_path in file_sorted_reverse[:n_files]]\n", + "print(full_paths)\n", + "try: # Note: sometimes multiple files cannot be merged, so we used try except here.\n", + " ds_mutiple = xr.open_mfdataset(full_paths)\n", + " # ds_mutiple\n", + " print(type(ds_mutiple))\n", + " print(ds_mutiple)\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "3fa59943", + "metadata": {}, + "source": [ + "## Explore the data" + ] + }, + { + "cell_type": "markdown", + "id": "571f69b9", + "metadata": {}, + "source": [ + "### NetCDF Data structure \n", + "\n", + "\n", + "(If you are confident with NetCDF basics and xarray essentials, feel free to skip this session.)\n", + "Before we dive into data exploration, there are some eseential concpets we should be familiar with (shown below): \n", + "* Dataset\n", + "* Data array\n", + "* Variable\n", + "* Dimenssion\n", + "* Coordinate\n", + "* Data Type\n", + "* Meta Data (Attributes)\n", + "\n", + "We will not go into details about NetCDF basics and here are some references you might find helpful\n", + "* [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#)\n", + "* [Network Common Data Form (NetCDF)](https://www.unidata.ucar.edu/software/netcdf/) and [A Brief History of (netCDF) Time](https://www.unidata.ucar.edu/software/netcdf/time/recs.html)\n", + "\n", + "\n", + "\n", + "### Xarray essentials\n", + "Earlier we introduced the xarray pacakge and used `open_dataset` and `open_mfdataset` to retrieve NetCDF data file as an xarray Dataset object. Recall that you can review the data in a notebook by using `print(ds)` or `ds`. \n", + "\n", + "Using xarray to retrieve the aforementioned NetCDF basics is straightforwared. In general,\n", + "* Dataset: `ds`\n", + "* Data array: `ds.variables`\n", + "* Variable: `ds.ds.variables`\n", + "* Dimenssion: `ds.dims`\n", + "* Coordinate: `ds.coords`\n", + "* Data Type: `type`\n", + "* Meta Data (Attributes)\n", + "\n", + "Also, here are some references if you are new to xarray\n", + "* [Xarray in 45 minutes](https://tutorial.xarray.dev/overview/xarray-in-45-min.html)\n", + "* [Handling NetCDF Files using XArray for Absolute Beginners](https://towardsdatascience.com/handling-netcdf-files-using-xarray-for-absolute-beginners-111a8ab4463f)\n", + "\n", + "Try the following commands in action\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b3eac323", + "metadata": {}, + "outputs": [], + "source": [ + "ds = ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77ecf85d", + "metadata": {}, + "outputs": [], + "source": [ + "# Dataset \n", + "ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25e7de09", + "metadata": {}, + "outputs": [], + "source": [ + "# Data array, Variable\n", + "# Note: the info can be overwhelming. Do not attempt to grasp everything at the first glance.\n", + "ds.variables" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c41b67e", + "metadata": {}, + "outputs": [], + "source": [ + "# dimenssions\n", + "ds.dims" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "156f1dfc", + "metadata": {}, + "outputs": [], + "source": [ + "# coordinates\n", + "ds.coords" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "277d6064", + "metadata": {}, + "outputs": [], + "source": [ + "# Meta Data (Attributes)\n", + "ds.attrs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d334681f", + "metadata": {}, + "outputs": [], + "source": [ + "# type\n", + "print(type(ds))\n", + "print(type(ds.variables))\n", + "print(type(ds.dims))\n", + "print(type(ds.coords))\n", + "print(type(ds.attrs))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "643399d6", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: retrieve variable names only\n", + "list(ds.variables)" + ] + }, + { + "cell_type": "markdown", + "id": "1cf36878", + "metadata": {}, + "source": [ + "#### Discussion: variable vs. coordinates vs. dimenssions. \n", + "From [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#):\n", + "\n", + "* Dimession: A dimension may be used to represent a real physical dimension, for example, time, latitude, longitude, or height. A dimension might also be used to index other quantities, for example station or model-run-number.\n", + "A netCDF dimension has both a name and a length. A dimension length is an arbitrary positive integer, except that one dimension in a netCDF dataset can have the length UNLIMITED.\n", + "\n", + "* Variables: Variables are used to store the bulk of the data in a netCDF dataset. A variable represents an array of values of the same type. A scalar value is treated as a 0-dimensional array. A variable has a name, a data type, and a shape described by its list of dimensions specified when the variable is created. A variable may also have associated attributes, which may be added, deleted or changed after the variable is created.\n", + "\n", + "* Coordinate (Variables): It is legal for a variable to have the same name as a dimension. Such variables have no special meaning to the netCDF library. However there is a convention that such variables should be treated in a special way by software using this library.\n", + "A variable with the same name as a dimension is called a coordinate variable. It typically defines a physical coordinate corresponding to that dimension.\n", + "\n", + "\n", + "Tips: By definition, coordinates and dimenssions are also variable. For example ds.time is a coordinate but it is also a special variable. But in practice, when we talk about variable, it implies regular/non-coordinate variable. We will use this convention for the remaining notebook. " + ] + }, + { + "cell_type": "markdown", + "id": "d47120f9", + "metadata": {}, + "source": [ + "### Xarray Variable\n", + "\n", + "\n", + "We can use `ds.variables` to access varialbes of a dataset. But many times viewing print-out the whole datasets can be overwhemling. Instead, we would work on the individual variable (also called Xarray data array.) using the `ds[var_name]` syntax, where `var_name` is the name of the variable. See the following examples in action.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5a57e136", + "metadata": {}, + "outputs": [], + "source": [ + "print(type(ds[\"time\"]))\n", + "ds[\"time\"]" + ] + }, + { + "cell_type": "markdown", + "id": "51ec643e", + "metadata": {}, + "source": [ + "#### Variable properties\n", + "For individual varible, the following properties are mostly used with the assocaited xarray method to retrieve them. (assuming var = ds[var_name])\n", + "* name: `var.name`\n", + "* data content: `var.data`\n", + "* attributes: `var.attrs`\n", + "* dimenstions: `var.dims`\n", + "* data type: `var.data.dtype`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a632a525", + "metadata": {}, + "outputs": [], + "source": [ + "var_name = \"time\"\n", + "var = ds[var_name]\n", + "\n", + "print(\"var.name: \\n\", var.name, \"\\n\")\n", + "print(\"var.data: \\n\", var.data, \"\\n\")\n", + "print(\"var.attrs: \\n\", var.attrs, \"\\n\")\n", + "print(\"var.dims: \\n\", var.dims, \"\\n\")\n", + "print(\"var.data.dtype: \\n\", var.data.dtype, \"\\n\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b256b07f", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: indivdual variable info table (collect and display variable info as a tabular format)\n", + "df_info = pd.DataFrame()\n", + "df_info[\"var_name\"] = list(ds.variables)\n", + "df_info[\"dims\"] = df_info.var_name.apply(lambda x: ds[x].dims)\n", + "df_info[\"is_dim\"] = df_info.var_name.apply(lambda x: x in ds.dims)\n", + "df_info[\"n_dim\"] = df_info.var_name.apply(lambda x: len(ds[x].dims))\n", + "df_info[\"attrs\"] = df_info.var_name.apply(lambda x: ds[x].attrs)\n", + "df_info[\"dtype\"] = df_info.var_name.apply(lambda x: ds[x].data.dtype)\n", + "df_info" + ] + }, + { + "cell_type": "markdown", + "id": "e6a36b96", + "metadata": {}, + "source": [ + "### Data cleaning/Preprocessing (skipped)\n", + "Data cleaning and preprocessing is an important stage in the the data analysis pipeline. However it will be out of the scope of this notebook. For more information about data cleaning and preprocessing basics, here are some references. \n", + "* [Xarray Fundamentals](https://earth-env-data-science.github.io/lectures/xarray/xarray.html)\n", + "* [Xarray Tutorial — Pangeo Gallery documentation](http://gallery.pangeo.io/repos/pangeo-data/pangeo-tutorial-gallery/xarray.html)\n", + "* [Pythonic Data Cleaning With pandas and NumPy](https://realpython.com/python-data-cleaning-numpy-pandas/)\n", + "* [Pandas - Cleaning Data](https://www.w3schools.com/python/pandas/pandas_cleaning.asp)" + ] + }, + { + "cell_type": "markdown", + "id": "ae6e1a69", + "metadata": {}, + "source": [ + "### Plotting\n", + "Data visualization (or plotting) is another important data analysis topic and deserves its own discussion. Here in this notebook we will only demonstrate basic tool for simple data visualization tasks.\n", + "\n", + "Here are some reference you might find useful:\n", + "* xarray plotting: https://docs.xarray.dev/en/stable/user-guide/plotting.html\n", + "* Atmospheric Community Toolkit (ACT): https://arm-doe.github.io/ACT/index.html\n", + "\n", + "Note: this notebook is auto-generated using a template. It uses a general idea to select variable(s) to plot and is not customized to each indivdual notebook. Feel free to change the variables in intrrests, especially certain figure is failed to plot." + ] + }, + { + "cell_type": "markdown", + "id": "7e417fb2", + "metadata": {}, + "source": [ + "#### 1-dimenssional basic time series plot\n", + "\n", + "For the following plot we would like to find variables such that\n", + "* it has one and only one dimession\n", + "* \"time\" is its coordinate variable\n", + "* it is not a dimenssion itself,\n", + "* it is not a special variable with substrings within [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "\n", + "Please see the following example in action. (For more details about pandas filtering, please see the following references.)\n", + "* [pandas: multiple conditions while indexing data frame](https://stackoverflow.com/questions/22591174/pandas-multiple-conditions-while-indexing-data-frame-unexpected-behavior)\n", + "* [pandas.DataFrame.apply](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.apply.html)\n", + "* [How to test if a string contains one of the substrings in a list...](https://stackoverflow.com/questions/26577516/how-to-test-if-a-string-contains-one-of-the-substrings-in-a-list-in-pandas" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30edac2d", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==1, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter = df_info[(df_info.n_dim == 1) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4777a659", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_1d = df_filter.var_name.values[0]\n", + " var_1d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb5ae985", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " ds[var_1d].plot()\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "9e41066e", + "metadata": {}, + "source": [ + "#### 2-dimenssional basic plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09bd4adc", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==2, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_2 = df_info[(df_info.n_dim == 2) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2cdea1a9", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_2d = df_filter_2.var_name.values[0]\n", + " var_2d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5f48879", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " print(ds[var_2d].dims)\n", + " # ds[var_2d].plot()\n", + "\n", + " # conventionally, use \"time\" as x-axis\n", + " ds[var_2d].plot(x=\"time\")\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "7554b2a6", + "metadata": {}, + "source": [ + "#### qc-plotting (optional)\n", + "\n", + "Note: act qc plotting has more strict requirement, one of them is the associated qc variable need to have \"flag_masks\" attributes. Which is added by using ds = act.io.armfiles.read_netcdf(files_list), then ds.clean.cleanup()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71c2096f", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " ds_act = act.io.armfiles.read_netcdf(full_path)\n", + " print(type(ds_act))\n", + " ds_act.clean.cleanup()\n", + "\n", + " # or \n", + " # ds.clean.cleanup()\n", + "except Exception as e:\n", + " print(\"ERROR\", e)\n", + " ds_act = ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "844f8505", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter valid variables for ACT qc plotting\n", + "condition = (\"qc_\" + df_info.var_name).apply(lambda x: ds_act[x].attrs.get(\"flag_masks\") is not None \n", + " if x in list(ds.variables) else False)\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_3 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings))) &\n", + " condition\n", + " ]\n", + "df_filter_3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd38cc6", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "try:\n", + " qc_variable = df_filter_3.var_name.values[0]\n", + " print(qc_variable)\n", + "except Exception as e:\n", + " print(e)\n", + " \n", + "try:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + " qc_ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "d76e4d27", + "metadata": {}, + "source": [ + "#### bonus: choose variables to plot from a dropdown menu " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb733804", + "metadata": {}, + "outputs": [], + "source": [ + "# Valid variables filtering\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_4 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82d5c20d", + "metadata": {}, + "outputs": [], + "source": [ + "# example 1: using xarray plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2fc9aaa4", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# fig, ax = plt.subplots(figsize=(10, 4))\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "# fig.clear() # Remove old lines from plot and plot new one\n", + "# if len(ds[var].dims)==2:\n", + "# ds[var].plot(x=\"time\", add_colorbar=False)\n", + "# else:\n", + "# ds[var].plot()\n", + "# plt.grid()\n", + "# plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fabdd802", + "metadata": {}, + "outputs": [], + "source": [ + "# example 2: using act plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "962b4186", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "\n", + "\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "\n", + "# i_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + "# i_display.add_subplots((1,), figsize=(10, 4))\n", + "# ax = i_display.plot(var, subplot_index=(0,), set_title=f\"{var} ({ds_act[var].attrs['long_name']})\",)\n", + "\n", + "# ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "# ax.grid()\n", + "# plt.show()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/AOD-NIMFR/nimfraod1mich.c1.ipynb b/VAPs/quicklook/AOD-NIMFR/nimfraod1mich.c1.ipynb new file mode 100644 index 00000000..5753a21c --- /dev/null +++ b/VAPs/quicklook/AOD-NIMFR/nimfraod1mich.c1.ipynb @@ -0,0 +1,339 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# NIMFRAOD1MICH.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/aod-nimfr) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'nimfraod1mich'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2017-07-27', 'facility': 'C1', 'site': 'nsa', 'start_date': '1998-04-28'}, {'end_date': '2010-11-12', 'facility': 'C2', 'site': 'nsa', 'start_date': '2000-04-28'}, {'end_date': '2017-12-14', 'facility': 'C1', 'site': 'sgp', 'start_date': '2000-05-03'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2017-12-14'\n", + "date_end = '2017-12-14'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['direct_normal_narrowband_filter1', 'direct_normal_narrowband_filter2', 'direct_normal_narrowband_filter3']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'direct_normal_broadband'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'direct_normal_narrowband_filter1'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/AOD/AOD_tutorial.ipynb b/VAPs/quicklook/AOD/AOD_tutorial.ipynb new file mode 100644 index 00000000..14938a88 --- /dev/null +++ b/VAPs/quicklook/AOD/AOD_tutorial.ipynb @@ -0,0 +1,838 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# SASHENIRAOD.C1 Notebook\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/aod) for more information about this vap." + ] + }, + { + "cell_type": "markdown", + "id": "97097763", + "metadata": {}, + "source": [ + "In this notebook, we demonstrate the workflow to explore ARM vap data (using sasheniraod as an example.) Value-added products (VAPs) are higher-order data products that have been analyzed and processed to ease scientists’ use of ARM data in atmospheric research and global climate models. \n", + "Here is the main content we will cover." + ] + }, + { + "cell_type": "markdown", + "id": "eddec40f", + "metadata": {}, + "source": [ + "# Table of Content\n", + "## Access the data\n", + "* How to retrieve the data\n", + "* Data path and file name conventions\n", + "* Load data\n", + "## Explore the data\n", + "* NetCDF Data structure\n", + "* Xarray essentials\n", + "* Xarray Variable\n", + "## Plot the data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "import random\n", + "\n", + "import glob\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "2372d5be", + "metadata": {}, + "source": [ + "## Access the data" + ] + }, + { + "cell_type": "markdown", + "id": "5b0d9684", + "metadata": {}, + "source": [ + "### How to retrieve the data\n", + "We assume the path \"/data/archive\" is available where you are running this notebook. You can use `os.path.exists(\"/data/archive\")` to verify if the path exists at your machine.\n", + "\n", + "\n", + "### Data path and file name conventions\n", + "There several common terminologies regarding ARM data, for example, data-stream-name, data-level, site, facility, instrument, etc. (For more details, please see [ARM Data File Standards Version 1.2](https://www.google.com/search?q=arm+datastream+facility+cite+definition&rlz=1C1GCEJ_enUS1029US1029&ei=hb41ZICrBPukqtsPvMODIA&ved=0ahUKEwjAgeiS0aL-AhV7kmoFHbzhAAQQ4dUDCBA&uact=5&oq=arm+datastream+facility+cite+definition&gs_lcp=Cgxnd3Mtd2l6LXNlcnAQAzIFCAAQogQyBQgAEKIEMgUIABCiBDIFCAAQogQyBQgAEKIEOgoIABBHENYEELADOgoIIRCgARDDBBAKSgQIQRgAUJ4DWIoOYO0PaAFwAXgAgAGMAYgBuQmSAQMyLjmYAQCgAQHIAQjAAQE&sclient=gws-wiz-serp).) \n", + "\n", + "For example, this notebook is called `sasheniraod.c1`, where `sasheniraod` is the \"datastream name\", and the `{process.ds_class_level}}` is the \"data level\".\n", + "\n", + "This datastream also contains site `hou` and facility `M1`. (Note: individual datastream might have multiple site-facility pairs.)\n", + "In such a case, the data of this data-stream is stored at `/data/archive/hou/housasheniraodM1.c1`, which is in the format of `//.`. We can use the following method to assign the data-stream directory `datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )`\n", + "\n", + "The data files under datastream_dir also follows naming conventions. But once reach the datastream_dir level, the most import file naming convention to differentiate the files is \"yyyyMMdd.hhmmss\", which comes handy to filter out files based on datetime. For example, we can use `glob.glob(f'{datastream_dir}/*.200709*.*')` to filter files in 2007 September.\n", + "\n", + "(Note: refer to the https://adc.arm.gov/discovery/#/ and https://adc.arm.gov/solr8/metadata/select API to explore ARM datastream and assoicated available sites and facilities\n", + "\n", + "Please see the following examples in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d7e9eb85", + "metadata": {}, + "outputs": [], + "source": [ + "# Verify if DATA_DIR path exists\n", + "DATA_DIR = \"/data/archive\"\n", + "os.path.exists(DATA_DIR)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "586993fd", + "metadata": {}, + "outputs": [], + "source": [ + "# Speicify datastream_dir following the path conventions and check its existence\n", + "DATASTREAM_NAME = \"sasheniraod\"\n", + "DATA_LEVEL = \"c1\"\n", + "site = \"hou\"\n", + "facility = \"M1\"\n", + "datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )\n", + "print(datastream_dir)\n", + "print(os.path.exists(datastream_dir))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0742f7c1", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: list 5 (random) files under datastream_dir\n", + "files = os.listdir(datastream_dir)\n", + "files[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39b98a36", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: get most recent file\n", + "list_of_files = glob.glob(f\"{datastream_dir}/*\") # * means all if need specific format then *.csv\n", + "latest_file = max(list_of_files, key=os.path.getctime)\n", + "latest_file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "902d514e", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus sort datastream files based on datetime\n", + "files = os.listdir(datastream_dir)\n", + "file_sorted = files.copy()\n", + "file_sorted.sort() \n", + "print(file_sorted[:5])\n", + "\n", + "# to reverse\n", + "file_sorted_reverse = files.copy() \n", + "file_sorted_reverse.sort(reverse=True)\n", + "print(file_sorted_reverse[:5])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec5923b2", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: pattern matching\n", + "# filter the 200709** files under datastream_dir\n", + "files_filter = glob.glob(f'{datastream_dir}/*.200709*.*')\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c4a4aa18", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: use armnotebook_utils.file_filter (TODO) to filter files based on datastream info \n" + ] + }, + { + "cell_type": "markdown", + "id": "cfeb9efc", + "metadata": {}, + "source": [ + "### Load data\n", + "The arm data is stored in [NetCDF](https://en.wikipedia.org/wiki/NetCDF#:~:text=%22NetCDF%20(network%20Common%20Data%20Form,format%20for%20representing%20scientific%20data.) format. We can use xarray's `open_dataset` method to load single file, or `open_mfdataset` to open multiple files. (Note: the latter will still return a single xarray dataset object by combining multiple files.)\n", + "\n", + "Note: open_dataset keeps the file handle open and lazy loads its contents. All parameters are passed directly to open_dataset. It is a preferable method over load_dataset for memory-effiency. (more details, please see [xarray.open_dataset](https://docs.xarray.dev/en/stable/generated/xarray.open_dataset.html))\n", + "\n", + "See the following example in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a440a329", + "metadata": {}, + "outputs": [], + "source": [ + "# open single file\n", + "full_path = latest_file\n", + "print(full_path)\n", + "ds_single = xr.open_dataset(full_path)\n", + "ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0143a3d", + "metadata": {}, + "outputs": [], + "source": [ + "print(ds_single)\n", + "print(type(ds_single))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c0f8939", + "metadata": {}, + "outputs": [], + "source": [ + "# open multiple files\n", + "n_files = 3\n", + "full_paths = [os.path.join(datastream_dir, f_path) for f_path in file_sorted_reverse[:n_files]]\n", + "print(full_paths)\n", + "try: # Note: sometimes multiple files cannot be merged, so we used try except here.\n", + " ds_mutiple = xr.open_mfdataset(full_paths)\n", + " # ds_mutiple\n", + " print(type(ds_mutiple))\n", + " print(ds_mutiple)\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "3fa59943", + "metadata": {}, + "source": [ + "## Explore the data" + ] + }, + { + "cell_type": "markdown", + "id": "571f69b9", + "metadata": {}, + "source": [ + "### NetCDF Data structure \n", + "\n", + "\n", + "(If you are confident with NetCDF basics and xarray essentials, feel free to skip this session.)\n", + "Before we dive into data exploration, there are some eseential concpets we should be familiar with (shown below): \n", + "* Dataset\n", + "* Data array\n", + "* Variable\n", + "* Dimenssion\n", + "* Coordinate\n", + "* Data Type\n", + "* Meta Data (Attributes)\n", + "\n", + "We will not go into details about NetCDF basics and here are some references you might find helpful\n", + "* [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#)\n", + "* [Network Common Data Form (NetCDF)](https://www.unidata.ucar.edu/software/netcdf/) and [A Brief History of (netCDF) Time](https://www.unidata.ucar.edu/software/netcdf/time/recs.html)\n", + "\n", + "\n", + "\n", + "### Xarray essentials\n", + "Earlier we introduced the xarray pacakge and used `open_dataset` and `open_mfdataset` to retrieve NetCDF data file as an xarray Dataset object. Recall that you can review the data in a notebook by using `print(ds)` or `ds`. \n", + "\n", + "Using xarray to retrieve the aforementioned NetCDF basics is straightforwared. In general,\n", + "* Dataset: `ds`\n", + "* Data array: `ds.variables`\n", + "* Variable: `ds.ds.variables`\n", + "* Dimenssion: `ds.dims`\n", + "* Coordinate: `ds.coords`\n", + "* Data Type: `type`\n", + "* Meta Data (Attributes)\n", + "\n", + "Also, here are some references if you are new to xarray\n", + "* [Xarray in 45 minutes](https://tutorial.xarray.dev/overview/xarray-in-45-min.html)\n", + "* [Handling NetCDF Files using XArray for Absolute Beginners](https://towardsdatascience.com/handling-netcdf-files-using-xarray-for-absolute-beginners-111a8ab4463f)\n", + "\n", + "Try the following commands in action\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b3eac323", + "metadata": {}, + "outputs": [], + "source": [ + "ds = ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77ecf85d", + "metadata": {}, + "outputs": [], + "source": [ + "# Dataset \n", + "ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25e7de09", + "metadata": {}, + "outputs": [], + "source": [ + "# Data array, Variable\n", + "# Note: the info can be overwhelming. Do not attempt to grasp everything at the first glance.\n", + "ds.variables" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c41b67e", + "metadata": {}, + "outputs": [], + "source": [ + "# dimenssions\n", + "ds.dims" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "156f1dfc", + "metadata": {}, + "outputs": [], + "source": [ + "# coordinates\n", + "ds.coords" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "277d6064", + "metadata": {}, + "outputs": [], + "source": [ + "# Meta Data (Attributes)\n", + "ds.attrs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d334681f", + "metadata": {}, + "outputs": [], + "source": [ + "# type\n", + "print(type(ds))\n", + "print(type(ds.variables))\n", + "print(type(ds.dims))\n", + "print(type(ds.coords))\n", + "print(type(ds.attrs))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "643399d6", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: retrieve variable names only\n", + "list(ds.variables)" + ] + }, + { + "cell_type": "markdown", + "id": "1cf36878", + "metadata": {}, + "source": [ + "#### Discussion: variable vs. coordinates vs. dimenssions. \n", + "From [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#):\n", + "\n", + "* Dimession: A dimension may be used to represent a real physical dimension, for example, time, latitude, longitude, or height. A dimension might also be used to index other quantities, for example station or model-run-number.\n", + "A netCDF dimension has both a name and a length. A dimension length is an arbitrary positive integer, except that one dimension in a netCDF dataset can have the length UNLIMITED.\n", + "\n", + "* Variables: Variables are used to store the bulk of the data in a netCDF dataset. A variable represents an array of values of the same type. A scalar value is treated as a 0-dimensional array. A variable has a name, a data type, and a shape described by its list of dimensions specified when the variable is created. A variable may also have associated attributes, which may be added, deleted or changed after the variable is created.\n", + "\n", + "* Coordinate (Variables): It is legal for a variable to have the same name as a dimension. Such variables have no special meaning to the netCDF library. However there is a convention that such variables should be treated in a special way by software using this library.\n", + "A variable with the same name as a dimension is called a coordinate variable. It typically defines a physical coordinate corresponding to that dimension.\n", + "\n", + "\n", + "Tips: By definition, coordinates and dimenssions are also variable. For example ds.time is a coordinate but it is also a special variable. But in practice, when we talk about variable, it implies regular/non-coordinate variable. We will use this convention for the remaining notebook. " + ] + }, + { + "cell_type": "markdown", + "id": "d47120f9", + "metadata": {}, + "source": [ + "### Xarray Variable\n", + "\n", + "\n", + "We can use `ds.variables` to access varialbes of a dataset. But many times viewing print-out the whole datasets can be overwhemling. Instead, we would work on the individual variable (also called Xarray data array.) using the `ds[var_name]` syntax, where `var_name` is the name of the variable. See the following examples in action.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5a57e136", + "metadata": {}, + "outputs": [], + "source": [ + "print(type(ds[\"time\"]))\n", + "ds[\"time\"]" + ] + }, + { + "cell_type": "markdown", + "id": "51ec643e", + "metadata": {}, + "source": [ + "#### Variable properties\n", + "For individual varible, the following properties are mostly used with the assocaited xarray method to retrieve them. (assuming var = ds[var_name])\n", + "* name: `var.name`\n", + "* data content: `var.data`\n", + "* attributes: `var.attrs`\n", + "* dimenstions: `var.dims`\n", + "* data type: `var.data.dtype`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a632a525", + "metadata": {}, + "outputs": [], + "source": [ + "var_name = \"time\"\n", + "var = ds[var_name]\n", + "\n", + "print(\"var.name: \\n\", var.name, \"\\n\")\n", + "print(\"var.data: \\n\", var.data, \"\\n\")\n", + "print(\"var.attrs: \\n\", var.attrs, \"\\n\")\n", + "print(\"var.dims: \\n\", var.dims, \"\\n\")\n", + "print(\"var.data.dtype: \\n\", var.data.dtype, \"\\n\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b256b07f", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: indivdual variable info table (collect and display variable info as a tabular format)\n", + "df_info = pd.DataFrame()\n", + "df_info[\"var_name\"] = list(ds.variables)\n", + "df_info[\"dims\"] = df_info.var_name.apply(lambda x: ds[x].dims)\n", + "df_info[\"is_dim\"] = df_info.var_name.apply(lambda x: x in ds.dims)\n", + "df_info[\"n_dim\"] = df_info.var_name.apply(lambda x: len(ds[x].dims))\n", + "df_info[\"attrs\"] = df_info.var_name.apply(lambda x: ds[x].attrs)\n", + "df_info[\"dtype\"] = df_info.var_name.apply(lambda x: ds[x].data.dtype)\n", + "df_info" + ] + }, + { + "cell_type": "markdown", + "id": "e6a36b96", + "metadata": {}, + "source": [ + "### Data cleaning/Preprocessing (skipped)\n", + "Data cleaning and preprocessing is an important stage in the the data analysis pipeline. However it will be out of the scope of this notebook. For more information about data cleaning and preprocessing basics, here are some references. \n", + "* [Xarray Fundamentals](https://earth-env-data-science.github.io/lectures/xarray/xarray.html)\n", + "* [Xarray Tutorial — Pangeo Gallery documentation](http://gallery.pangeo.io/repos/pangeo-data/pangeo-tutorial-gallery/xarray.html)\n", + "* [Pythonic Data Cleaning With pandas and NumPy](https://realpython.com/python-data-cleaning-numpy-pandas/)\n", + "* [Pandas - Cleaning Data](https://www.w3schools.com/python/pandas/pandas_cleaning.asp)" + ] + }, + { + "cell_type": "markdown", + "id": "ae6e1a69", + "metadata": {}, + "source": [ + "### Plotting\n", + "Data visualization (or plotting) is another important data analysis topic and deserves its own discussion. Here in this notebook we will only demonstrate basic tool for simple data visualization tasks.\n", + "\n", + "Here are some reference you might find useful:\n", + "* xarray plotting: https://docs.xarray.dev/en/stable/user-guide/plotting.html\n", + "* Atmospheric Community Toolkit (ACT): https://arm-doe.github.io/ACT/index.html\n", + "\n", + "Note: this notebook is auto-generated using a template. It uses a general idea to select variable(s) to plot and is not customized to each indivdual notebook. Feel free to change the variables in intrrests, especially certain figure is failed to plot." + ] + }, + { + "cell_type": "markdown", + "id": "7e417fb2", + "metadata": {}, + "source": [ + "#### 1-dimenssional basic time series plot\n", + "\n", + "For the following plot we would like to find variables such that\n", + "* it has one and only one dimession\n", + "* \"time\" is its coordinate variable\n", + "* it is not a dimenssion itself,\n", + "* it is not a special variable with substrings within [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "\n", + "Please see the following example in action. (For more details about pandas filtering, please see the following references.)\n", + "* [pandas: multiple conditions while indexing data frame](https://stackoverflow.com/questions/22591174/pandas-multiple-conditions-while-indexing-data-frame-unexpected-behavior)\n", + "* [pandas.DataFrame.apply](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.apply.html)\n", + "* [How to test if a string contains one of the substrings in a list...](https://stackoverflow.com/questions/26577516/how-to-test-if-a-string-contains-one-of-the-substrings-in-a-list-in-pandas" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30edac2d", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==1, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter = df_info[(df_info.n_dim == 1) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4777a659", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_1d = df_filter.var_name.values[0]\n", + " var_1d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb5ae985", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " ds[var_1d].plot()\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "9e41066e", + "metadata": {}, + "source": [ + "#### 2-dimenssional basic plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09bd4adc", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==2, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_2 = df_info[(df_info.n_dim == 2) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2cdea1a9", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_2d = df_filter_2.var_name.values[0]\n", + " var_2d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5f48879", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " print(ds[var_2d].dims)\n", + " # ds[var_2d].plot()\n", + "\n", + " # conventionally, use \"time\" as x-axis\n", + " ds[var_2d].plot(x=\"time\")\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "7554b2a6", + "metadata": {}, + "source": [ + "#### qc-plotting (optional)\n", + "\n", + "Note: act qc plotting has more strict requirement, one of them is the associated qc variable need to have \"flag_masks\" attributes. Which is added by using ds = act.io.armfiles.read_netcdf(files_list), then ds.clean.cleanup()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71c2096f", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " ds_act = act.io.armfiles.read_netcdf(full_path)\n", + " print(type(ds_act))\n", + " ds_act.clean.cleanup()\n", + "\n", + " # or \n", + " # ds.clean.cleanup()\n", + "except Exception as e:\n", + " print(\"ERROR\", e)\n", + " ds_act = ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "844f8505", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter valid variables for ACT qc plotting\n", + "condition = (\"qc_\" + df_info.var_name).apply(lambda x: ds_act[x].attrs.get(\"flag_masks\") is not None \n", + " if x in list(ds.variables) else False)\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_3 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings))) &\n", + " condition\n", + " ]\n", + "df_filter_3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd38cc6", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "try:\n", + " qc_variable = df_filter_3.var_name.values[0]\n", + " print(qc_variable)\n", + "except Exception as e:\n", + " print(e)\n", + " \n", + "try:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + " qc_ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "d76e4d27", + "metadata": {}, + "source": [ + "#### bonus: choose variables to plot from a dropdown menu " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb733804", + "metadata": {}, + "outputs": [], + "source": [ + "# Valid variables filtering\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_4 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82d5c20d", + "metadata": {}, + "outputs": [], + "source": [ + "# example 1: using xarray plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2fc9aaa4", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# fig, ax = plt.subplots(figsize=(10, 4))\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "# fig.clear() # Remove old lines from plot and plot new one\n", + "# if len(ds[var].dims)==2:\n", + "# ds[var].plot(x=\"time\", add_colorbar=False)\n", + "# else:\n", + "# ds[var].plot()\n", + "# plt.grid()\n", + "# plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fabdd802", + "metadata": {}, + "outputs": [], + "source": [ + "# example 2: using act plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "962b4186", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "\n", + "\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "\n", + "# i_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + "# i_display.add_subplots((1,), figsize=(10, 4))\n", + "# ax = i_display.plot(var, subplot_index=(0,), set_title=f\"{var} ({ds_act[var].attrs['long_name']})\",)\n", + "\n", + "# ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "# ax.grid()\n", + "# plt.show()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/AOD/sasheniraod.c1.ipynb b/VAPs/quicklook/AOD/sasheniraod.c1.ipynb new file mode 100644 index 00000000..7ab77627 --- /dev/null +++ b/VAPs/quicklook/AOD/sasheniraod.c1.ipynb @@ -0,0 +1,339 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# SASHENIRAOD.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/aod) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'sasheniraod'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2022-10-01', 'facility': 'M1', 'site': 'hou', 'start_date': '2021-09-21'}, {'end_date': '2015-12-01', 'facility': 'M1', 'site': 'mao', 'start_date': '2014-01-01'}, {'end_date': '2013-06-21', 'facility': 'M1', 'site': 'pvc', 'start_date': '2012-06-27'}, {'end_date': '2019-07-27', 'facility': 'C1', 'site': 'sgp', 'start_date': '2011-03-22'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2019-07-26'\n", + "date_end = '2019-07-26'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['diffuse_transmittance', 'direct_normal_transmittance', 'aerosol_optical_depth']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'aerosol_optical_depth'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'diffuse_transmittance'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/AOD/sashevisaod.c1.ipynb b/VAPs/quicklook/AOD/sashevisaod.c1.ipynb new file mode 100644 index 00000000..62f677dd --- /dev/null +++ b/VAPs/quicklook/AOD/sashevisaod.c1.ipynb @@ -0,0 +1,339 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# SASHEVISAOD.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/aod) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'sashevisaod'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2022-10-01', 'facility': 'M1', 'site': 'hou', 'start_date': '2021-09-21'}, {'end_date': '2015-12-01', 'facility': 'M1', 'site': 'mao', 'start_date': '2014-01-01'}, {'end_date': '2013-06-21', 'facility': 'M1', 'site': 'pvc', 'start_date': '2012-06-27'}, {'end_date': '2019-07-27', 'facility': 'C1', 'site': 'sgp', 'start_date': '2011-03-22'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2019-07-26'\n", + "date_end = '2019-07-26'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['diffuse_transmittance', 'direct_normal_transmittance', 'aerosol_optical_depth']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'aerosol_optical_depth'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'diffuse_transmittance'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/AOP/.ipynb_checkpoints/aoppsap1flynn1m.c1-checkpoint.ipynb b/VAPs/quicklook/AOP/.ipynb_checkpoints/aoppsap1flynn1m.c1-checkpoint.ipynb new file mode 100644 index 00000000..927b1391 --- /dev/null +++ b/VAPs/quicklook/AOP/.ipynb_checkpoints/aoppsap1flynn1m.c1-checkpoint.ipynb @@ -0,0 +1,8265 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# AOPPSAP1FLYNN1M.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/aop) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'aoppsap1flynn1m'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2017-11-01', 'facility': 'M1', 'site': 'asi', 'start_date': '2016-04-23'}, {'end_date': '2020-06-01', 'facility': 'M1', 'site': 'anx', 'start_date': '2019-12-01'}, {'end_date': '2021-10-14', 'facility': 'M1', 'site': 'guc', 'start_date': '2021-09-02'}, {'end_date': '2023-06-15', 'facility': 'S2', 'site': 'guc', 'start_date': '2021-10-27'}, {'end_date': '2021-06-14', 'facility': 'M1', 'site': 'oli', 'start_date': '2016-08-06'}, {'end_date': '2019-04-30', 'facility': 'M1', 'site': 'cor', 'start_date': '2018-09-23'}, {'end_date': '2015-12-01', 'facility': 'S1', 'site': 'mao', 'start_date': '2014-02-06'}, {'end_date': '2018-01-11', 'facility': 'M1', 'site': 'mar', 'start_date': '2017-10-29'}, {'end_date': '2020-10-01', 'facility': 'M1', 'site': 'mos', 'start_date': '2019-10-11'}, {'end_date': '2023-12-05', 'facility': 'M1', 'site': 'epc', 'start_date': '2023-01-15'}, {'end_date': '2022-09-30', 'facility': 'M1', 'site': 'hou', 'start_date': '2021-09-08'}, {'end_date': '2023-12-11', 'facility': 'C1', 'site': 'ena', 'start_date': '2013-10-09'}, {'end_date': '2017-09-29', 'facility': 'C1', 'site': 'sgp', 'start_date': '2015-10-01'}, {'end_date': '2023-12-12', 'facility': 'E13', 'site': 'sgp', 'start_date': '2016-11-15'}]" + ] + }, + { + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "ac6764f5", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The following locations and date ranges are available for this VAP:\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
sitefacilitystart_dateend_date
0asiM12016-04-232017-11-01
1anxM12019-12-012020-06-01
2gucM12021-09-022021-10-14
3gucS22021-10-272023-06-15
4oliM12016-08-062021-06-14
5corM12018-09-232019-04-30
6maoS12014-02-062015-12-01
7marM12017-10-292018-01-11
8mosM12019-10-112020-10-01
9epcM12023-01-152023-12-05
10houM12021-09-082022-09-30
11enaC12013-10-092023-12-11
12sgpC12015-10-012017-09-29
13sgpE132016-11-152023-12-12
\n", + "
" + ], + "text/plain": [ + " site facility start_date end_date\n", + "0 asi M1 2016-04-23 2017-11-01\n", + "1 anx M1 2019-12-01 2020-06-01\n", + "2 guc M1 2021-09-02 2021-10-14\n", + "3 guc S2 2021-10-27 2023-06-15\n", + "4 oli M1 2016-08-06 2021-06-14\n", + "5 cor M1 2018-09-23 2019-04-30\n", + "6 mao S1 2014-02-06 2015-12-01\n", + "7 mar M1 2017-10-29 2018-01-11\n", + "8 mos M1 2019-10-11 2020-10-01\n", + "9 epc M1 2023-01-15 2023-12-05\n", + "10 hou M1 2021-09-08 2022-09-30\n", + "11 ena C1 2013-10-09 2023-12-11\n", + "12 sgp C1 2015-10-01 2017-09-29\n", + "13 sgp E13 2016-11-15 2023-12-12" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2017-09-27'\n", + "date_end = '2017-09-29'" + ] + }, + { + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'/data/archive/sgp/sgpaoppsap1flynn1mC1.c1'" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['20170927', '20170928', '20170929']" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "51feea2e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['/data/archive/sgp/sgpaoppsap1flynn1mC1.c1/sgpaoppsap1flynn1mC1.c1.20170927.000030.nc',\n", + " '/data/archive/sgp/sgpaoppsap1flynn1mC1.c1/sgpaoppsap1flynn1mC1.c1.20170928.000030.nc',\n", + " '/data/archive/sgp/sgpaoppsap1flynn1mC1.c1/sgpaoppsap1flynn1mC1.c1.20170929.000030.nc']" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "3 files loaded\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
<xarray.Dataset>\n",
+       "Dimensions:                         (time: 4320, bound: 2)\n",
+       "Coordinates:\n",
+       "  * time                            (time) datetime64[ns] 2017-09-27T00:00:30...\n",
+       "Dimensions without coordinates: bound\n",
+       "Data variables: (12/132)\n",
+       "    base_time                       (time) datetime64[ns] 2017-09-27 ... 2017...\n",
+       "    time_offset                     (time) datetime64[ns] 2017-09-27T00:00:30...\n",
+       "    time_bounds                     (time, bound) object dask.array<chunksize=(1440, 2), meta=np.ndarray>\n",
+       "    impactor_state                  (time) int32 10 10 10 ... -9999 -9999 -9999\n",
+       "    Bs_B                            (time) float32 dask.array<chunksize=(1440,), meta=np.ndarray>\n",
+       "    qc_Bs_B                         (time) int32 dask.array<chunksize=(1440,), meta=np.ndarray>\n",
+       "    ...                              ...\n",
+       "    K1_B                            (time) float32 dask.array<chunksize=(1440,), meta=np.ndarray>\n",
+       "    K1_G                            (time) float32 dask.array<chunksize=(1440,), meta=np.ndarray>\n",
+       "    K1_R                            (time) float32 dask.array<chunksize=(1440,), meta=np.ndarray>\n",
+       "    lat                             (time) float32 36.6 36.6 36.6 ... 36.6 36.6\n",
+       "    lon                             (time) float32 -97.49 -97.49 ... -97.49\n",
+       "    alt                             (time) float32 318.0 318.0 ... 318.0 318.0\n",
+       "Attributes: (12/20)\n",
+       "    command_line:                    aosaop -n aosaoppsap -s sgp -f C1 -D -b ...\n",
+       "    Conventions:                     ARM-1.2\n",
+       "    process_version:                 vap-aosaop-1.2-0.el6\n",
+       "    dod_version:                     aoppsap1flynn1m-c1-1.2\n",
+       "    input_datastreams:               sgpaosnephdry1mC1.b1 : 1.0 : 20170927.00...\n",
+       "    site_id:                         sgp\n",
+       "    ...                              ...\n",
+       "    doi:                             10.5439/1369240\n",
+       "    history:                         created by user dsmgr on machine ruby at...\n",
+       "    _file_dates:                     ['20170927', '20170928', '20170929']\n",
+       "    _file_times:                     ['000030', '000030', '000030']\n",
+       "    _datastream:                     sgpaoppsap1flynn1mC1.c1\n",
+       "    _arm_standards_flag:             1
" + ], + "text/plain": [ + "\n", + "Dimensions: (time: 4320, bound: 2)\n", + "Coordinates:\n", + " * time (time) datetime64[ns] 2017-09-27T00:00:30...\n", + "Dimensions without coordinates: bound\n", + "Data variables: (12/132)\n", + " base_time (time) datetime64[ns] 2017-09-27 ... 2017...\n", + " time_offset (time) datetime64[ns] 2017-09-27T00:00:30...\n", + " time_bounds (time, bound) object dask.array\n", + " impactor_state (time) int32 10 10 10 ... -9999 -9999 -9999\n", + " Bs_B (time) float32 dask.array\n", + " qc_Bs_B (time) int32 dask.array\n", + " ... ...\n", + " K1_B (time) float32 dask.array\n", + " K1_G (time) float32 dask.array\n", + " K1_R (time) float32 dask.array\n", + " lat (time) float32 36.6 36.6 36.6 ... 36.6 36.6\n", + " lon (time) float32 -97.49 -97.49 ... -97.49\n", + " alt (time) float32 318.0 318.0 ... 318.0 318.0\n", + "Attributes: (12/20)\n", + " command_line: aosaop -n aosaoppsap -s sgp -f C1 -D -b ...\n", + " Conventions: ARM-1.2\n", + " process_version: vap-aosaop-1.2-0.el6\n", + " dod_version: aoppsap1flynn1m-c1-1.2\n", + " input_datastreams: sgpaosnephdry1mC1.b1 : 1.0 : 20170927.00...\n", + " site_id: sgp\n", + " ... ...\n", + " doi: 10.5439/1369240\n", + " history: created by user dsmgr on machine ruby at...\n", + " _file_dates: ['20170927', '20170928', '20170929']\n", + " _file_times: ['000030', '000030', '000030']\n", + " _datastream: sgpaoppsap1flynn1mC1.c1\n", + " _arm_standards_flag: 1" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['Bs_B', 'Bs_G', 'Bs_R']" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "49f2c7463c164a46aa13bd6286315713", + "version_major": 2, + "version_minor": 0 + }, + "image/png": "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SRHr168X69evF5s3bxZCCHHu3Dnx4IMPiq+//lqsWLFCLFmyRDzzzDPCz89PfPnll0IpPj5eDBs2THjiOt+hQ4cEADFjxgzHtMWLFws/Pz/RtWtXMX/+fPHDDz+Idu3aiYSEBAHAaXkAREJCgmjbtq34/vvvxaJFi0TXrl1FlSpVxIEDB4QQQhw5ckQ88cQTAoCYN2+e43vabDbN9WzYsKGoV6+e+Prrr8Xq1avF3LlzxdNPPy1WrlzpmOell14SAMSAAQPEDz/8IJYtWybeffdd8dJLLznm+eSTT0RqaqpYuHChWL16tfjyyy9F8+bNRcOGDcXly5c9rp/e9snJyREtWrQQ0dHR4t133xW//vqr+OCDD4TZbBbdu3cXBQUFTr9T7dq1RbNmzcSsWbPEihUrxM6dOx2vvfLKK455Z8yYIQCIunXriieeeEIsXbpUfP755yIyMlJ069bN6XcaMmSI8PPzE2PHjhXLli0T77//voiNjRVms9nj/pCXlye6desmqlSpIp555hmxaNEisXDhQjF+/Hgxe/ZsIYQwvB8LIcQbb7whJEkSDz30kPj555/FvHnzRPv27UVoaKj4+++/hRBC/P333+LFF1907HPr168X+/fvF5s3bxYPP/ywACCWLFki1q9fL44cOSKEEKJLly6iS5cuxVpvIdz39eJur/LYr13ZbDbH9n7xxRcdy5C/+7Bhw0RAQIBISEgQqamp4rfffhNLly5V/X4y199r5cqVju9z7733il9++UXMnj1bxMXFifr164srV6445v3888+FJEmia9euYtasWeLXX38VH3/8sRg1apRjnh9++EG8/PLLYv78+WL16tXiu+++E126dBHVq1cXp0+fFkIIcerUKfHmm28KAGLq1KmO73Xq1CnH94qPj3csszj7WXx8vKhTp45o0qSJ+Oqrr8TSpUvF3XffLQCI1atX6/7eZb3Px8fHC4vFIurWrSu++OILsXLlSvHXX385fvPatWuLu+66SyxcuFD8/PPP4syZM+Lrr78WkiSJO+64Q8ybN0/89NNP4tZbbxX+/v7i119/dSx769atIiwsTCQkJIhp06aJ3377TXzzzTdi0KBBIjs72+NvbPRzfv31V+Hv7y86deok5s2bJ3744QfRpk0bERcX53a+N6o457BHH31UABCjR48WS5YsEdOmTRPVq1cXsbGx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1atXF5IkOe1/xbnGqDG6P547d0489NBDomrVqiIkJET07NlT7NmzR/O+Iz093THN6DXmnXfeER06dBDR0dGOc8jDDz/stCwh9O8BjWwjIQqHpJPP3Q0aNBBffPGF6n6vRu9e7vDhw2Lo0KGiWrVqIiAgQDRs2FC89dZbmvujktFzkBBF19233nrLafq2bdvEoEGDRI0aNURAQICoVauW6N69u5g2bZpjntzcXPHMM8+I2rVri+DgYNGyZUuxYMECt+//5Zdfim7duomaNWuKwMBAERMTIwYNGiS2b9/u9Jl//fWX6NChgwgNDRW1a9cWr7zyivj8889VqyKrfT8hCqv5PvnkkyIxMVEEBASIqKgo0apVK/HCCy+ICxcuOOaT7xuUx8y6desMVUU2+r3l33by5Mli4sSJok6dOiIwMFCkpKQ4ht8RovC++V//+pdo1qyZiIiIECaTSTRs2FC88sorThWshSjc3+Rro9lsFv3793cagkoI/fs0+RqzatUq0bp1axEUFCQsFosYP368WxXsX3/9VaSkpIigoCCnkU3Url2lXTetc6UaSQiNHD4fl5eX56j6tmzZMm+vDulYt24dOnbsiG+//bbMqx+ScYMGDcKhQ4eQlpbm7VUhKhNt27ZFfHy8owWBiAq9+eabePHFF5GRkVHiom/e9MEHH+Df//43zp8/7+iHSESl07VrV2RmZmLnzp3eXpUS8zzGio94+OGH0bNnT0caw7Rp07B792588MEH3l41Uli+fDnWr1+PVq1awWQyYdu2bZg0aRLq16+vWYiAyp8QAqtWrXLrA0Hkq7Kzs7Ft2zZ8+eWX3l4VIq/66KOPAACNGjVCXl4eVqxYgf/85z+47777fC6otdlsWL9+PWbOnImmTZsyqCUiJ9dNYHv+/Hk888wzOH36NAICAtCyZUssWrTIqfoheV9ERASWLVuG999/H+fPn0d0dDT69OmD1NRUt/LedO1IkoRTp055ezWIykxERES5VuQn8hUhISF47733kJ6ejtzcXMTFxeH555/Hiy++6O1VK7YtW7bgzjvvRLNmzRwjURARya7bVGQiIiIiIiKqHIwPqklERERERERUATGwJSIiIiIiIp/GwJaIiIiIiIh82nVTPIqcFRQU4Pjx4wgPD4ckSd5eHSIiIiKi65oQAufPn0dMTAz8/Nh+eK0xsL1OHT9+HLGxsd5eDSIiIiKiSuXIkSM+N5zW9YCB7XUqPDwcQOGBFRER4eW1ofKSl5eHZcuWoVevXggICPD26lA54rauPLitKw9u68qB27nyyM7ORmxsrOM+nK4tBrbXKTn9OCIigoHtdSwvLw8hISGIiIjgxfI6x21deXBbVx7c1pUDt3Plw26A3sHkbyIiIiIiIvJpDGyJiIiIiIjIpzGwJSIiIiIiIp/GwJaIiIiIiIh8GgPbayw1NRVt2rRBeHg4atSogTvuuAN79+51mkcIgQkTJiAmJgYmkwldu3bF33//7aU1JiIiIiIiqtgY2F5jq1evxuOPP44NGzZg+fLluHLlCnr16oWcnBzHPFOmTMG7776Ljz76CGlpaahVqxZ69uyJ8+fPe3HNiYiIiIiIKiYO93ONLVmyxOnvGTNmoEaNGti0aRM6d+4MIQTef/99vPDCCxgwYAAA4Msvv0TNmjUxa9YsjBw50hurTURERFSpWG12HMrMQWJ0KCxmk7dXh4g8YIutl9lsNgBAVFQUAODQoUM4ceIEevXq5ZgnKCgIXbp0wbp167yyjkRERESVyZy0DHSctAJDp/+JjpNWYE5ahrdXiYg8YIutFwkhMGbMGHTq1AlNmzYFAJw4cQIAULNmTad5a9asicOHD2suKzc3F7m5uY6/s7OzARQOCp6Xl1fWq04VhLxtuY2vf9zWlQe3deXBbV0xWW2XMG7eDhSIwr8LBDBu3g60T4yExRxc7OVxO1ce3MbexcDWi0aPHo3t27dj7dq1bq9JkuT0txDCbZpSamoqJk6c6DZ92bJlCAkJKf3KUoW2fPlyb68CXSPc1pUHt3XlwW1dsfxjk1Ag/J2mFQjgix9XIiValHi53M7Xv4sXL3p7FSo1BrZe8sQTT2DhwoVYs2YN6tSp45heq1YtAIUttxaLxTH91KlTbq24SuPGjcOYMWMcf2dnZyM2Nha9evVCREREOXwDqgjy8vKwfPly9OzZEwEBAd5eHSpH3NaVB7d15cFtXTFZbZfw8e41jhZb2Vf7/dHghia4u1Ud9Tdq4HauPOSMSfIOBrbXmBACTzzxBObPn49Vq1YhMTHR6fXExETUqlULy5cvR0pKCgDg8uXLWL16NSZPnqy53KCgIAQFBblNDwgI4Em0EuB2rjy4rSsPbuvKg9u6YomLDkDqgGQ8P3eH0/QCAbz04250a1yrRMWkuJ2vf9y+3sXiUdfY448/jm+++QazZs1CeHg4Tpw4gRMnTsButwMoTEH+97//jTfffBPz58/Hzp078eCDDyIkJARDhw718toTERERXf8Gt4lTnZ4vBNIzmW5KVBGxxfYa++STTwAAXbt2dZo+Y8YMPPjggwCA5557Dna7HaNGjUJWVhbatWuHZcuWITw8/BqvLREREVHl89vuE6rTJQAJ0axdQlQRMbC9xoTwXHRAkiRMmDABEyZMKP8VIiIiIiKHGX8cwsSfdnl7NYiomJiKTEREREQEwGqz6wa1AmAqMlEFxcCWiIiIiMqU1WbHugOZsNrs3l6VYtmYflb3dX9JYioyUQXFVGQiIiIiKjNz0jIwbt4OFAjATwJSByRrFmOqaCRJ0n29S4PoElVEJqLyxxZbIiIiIioTVpvdEdQChUPkjJ+302dablvFR+q+vnLvaZ/5LkSVDQNbIiIiIioThzJzHEGtzJeGyLGYTeiQFKX5ugCw+XDWtVshIjKMgS0RERERlYnE6FD4uWTz+lq/1A5J0bqvGxjggoi8gIEtEREREZUJi9mE5/s0cvztJwFvDmh63fRLlQC0StBPVyYi72BgS0RERERlYk5aBiYv3uP4e3DrWJ8pHCXbfSJb87U+ybWumyCd6HrDwJaIiIiISs21cBQAzNl4xOeKLWWez9V8benOkz73fYgqCwa2RERERFRqaoWjCgR8pnCU7GJugeZrvlQIi6iyYWBLRERERKWmVjjKT4JPFY6y2uzYcdymO09IIG+fiSoiHplEREREVGpr9p12qxjctWF176yMAVabHesOZDqlFh/KzPH4vouXtVt0ich7GNgSERERUalYbXaMnbcDriPhrNhzGh1SV2BOWoZX1kvLrD8Po0PqCgyd/ic6Tipav8ToUEge3rv92LlyXz8iKj4GtkRERERUKocyczTHdxUAxs3bUWGKLlltdrwwf6cjCC8QwPh5Ox3r52mY2imL91aY70JERRjYEhEREVGpJEaH6r5ekYpIHcrMcQte5aJQG9PPenw/C0gRVUwMbImIiIioVCxmE+rXCNN8vSIVkVILwiUUrp8keUpEBvwlqcJ8FyIqwsCWiIiIiEotwhSg+VrXBtVhMZuu4doU09V4tlV8pG4fWz8Abw5oWrG/C1ElxcCWiIiIiErNFKB9W7lq3+kK0y/1i7WH3KaJq6nSFrMJkwYmO4Ytch2+SHhu0CUiL6ni7RUgIiIiIt935Kx2v9MCReDoTVabHdN/dw9slanSg9vEoXOD6kjPvIiQQD/0n7rOMZ8QwNh5O9C5ordAE1VCbLElIiIiolKx2uw4fFa7RVaqIH1stYpD3dMmzilQtZhNaJ9UDUey3L+TEMDmw1nlto5EVDIMbImIiIioVA5l5ui+/njXpArRwqlVHKpjvWqq04XGGEZaQxsRkfcwsPWCNWvW4LbbbkNMTAwkScKCBQucXr9w4QJGjx6NOnXqwGQyoXHjxvjkk0+8s7JEREREHugN99Mitiqe6d3oGq6NtlbxkarTW2pMb50Q5VZMSgLQKkF9fiLyHga2XpCTk4PmzZvjo48+Un39qaeewpIlS/DNN99g9+7deOqpp/DEE0/gxx9/vMZrSkREROTZwq3HNV/75L6W13BNPKtqci4xEx0WqNma7CgmdfVvPwCTBiZXiNZnInLG4lFe0KdPH/Tp00fz9fXr12PYsGHo2rUrAODRRx/Fp59+io0bN6J///7XaC2JiIiIPLPa7Ji0eI/qa4/eVLfCBIFz0jIwdu4OuGYRZ164jLeW7sGzGq3KymJSCdEhFeb7EJEzBrYVUKdOnbBw4UI89NBDiImJwapVq7Bv3z588MEHmu/Jzc1Fbm6u4+/s7GwAQF5eHvLy8sp9nck75G3LbXz947auPLitK4/rZVvvP5HtFiwChSm797WrUyG+n9V2CePmuQe1sqkrDyA00A8jOiWqvh4dUgXRcREAir+9rpftTJ5xG3sXA9sK6D//+Q9GjBiBOnXqoEqVKvDz88Pnn3+OTp06ab4nNTUVEydOdJu+bNkyhIR4vwohla/ly5d7exXoGuG2rjy4rSsPX9/W53IBwB9w6o0q0CxKYMsfK7DFO6vl5B+bhALhrzvPW0v3IfT0blQNKp918PXtTJ5dvKg95BWVPwa2FdB//vMfbNiwAQsXLkR8fDzWrFmDUaNGwWKx4Oabb1Z9z7hx4zBmzBjH39nZ2YiNjUWvXr0QERFxrVadrrG8vDwsX74cPXv2REBAgLdXh8oRt3XlwW1deVwv29pqu4RXNq9xmSphR5aElI5dYTEHX9N1OXzmIuKrhTh9rtV2CVN3r9GtZiwgIanFjWiXGFWm63S9bGfyTM6YJO9gYFvB2O12jB8/HvPnz0e/fv0AAM2aNcPWrVvx9ttvawa2QUFBCApyf8QYEBDAk2glwO1ceXBbVx7c1pWHr2/rozab6vQCARyzXUZcdPg1WY85aRkYN28HCgTgJwGpA5IxuE0cACAuOgAjbqqLz9Yc1Hy/vyQhqWZEuW0LX9/O5Bm3r3exKnIFI/eJ9fNz3jT+/v4oKCjw0loRERERqdMa6sdPAhKiS98dymqzY92BTFhtdt155KAWKAyqx83d4fSe3ce1W9MkAG8OaMrCUEQ+jC22XnDhwgXs37/f8fehQ4ewdetWREVFIS4uDl26dMGzzz4Lk8mE+Ph4rF69Gl999RXeffddL641ERERkXG3NK2lGShabXYcysxBYnSobjCp1wqrdCgzxxHUygoAzFibjvH9GmPbkSz8vj9T83MEgHN2Fv4h8mVssfWCjRs3IiUlBSkpKQCAMWPGICUlBS+//DIA4LvvvkObNm1w7733okmTJpg0aRLeeOMN/Otf//LmahMRERG5OZSZozp98Y4Tqq2sn645gA6pKzB0+p/oOGkF5qRlqL5frRV2/LydqstMjA51Kl0l+3ztQVhtdkzXSUGWTV68R7dVmIgqNrbYekHXrl0hdKoX1KpVCzNmzLiGa0RERERUMlqpyALApvQs3Nq8qEX209UHkKoY81YOVjs3qO7WcqvWCpsvBNIzL7rNazGbMOKmRHz2+yGn6QWicB1+3nHC4/eQ540K89ySTEQVD1tsiYiIiKjELGYTaoQFqr4mKZpRrTY7JimCWpkcrLpSa4WVdPrtDlcZg9ZfkqDalKu2rgCe/G6Lbkuykf6+ROQdbLElIiIiolKpHRWCUxcuO02TJKBlfKTj70OZOdDKVwsJdG9rOZV9yW1+veF61uw77TateawZreIjIQGan63kmvasbEk22t+XiLyDLbZEREREVCpHzrq3uE4akOyUzquVsgwAFy87j/wwJy0D/aeuU513xtp0t2lWmx3Pz93hNn1zxjmcyr6EsX0aaX72s70b4KOhKW6Br7IluTj9fYnIOxjYEhEREVGJbTuShUyX1loAaFTL2Pi1rsMCWW12jFUJUmXTfz/oFlBuOpylOf/G9Czc3iJG9bVO9arh8W710UrRsizzlyTHeun19yWiioGBLRERERGV2F/pZ1Wnb0x3Dja1qic/0qmuU8uuXsoyUJhS7Npqq1eUMyE6RPOz/zhwBnPSMmAxmxAVWtRP2A/O49qq9fdVBr5E5H0MbImIiIioxNomRKlOb53g3AqaGB3qVEwKKCzYNLxTgtt8nsjD+BR9lvo6AEBIYABCA/1VXxMaKcWuYbLFbEK3RjWc1ntwmzoe15OIrh0GtkRERERUYs1jI9G+rnNgObBlbTSPdQ5sLWYTHuroXLn47tZ1SjSsToGA4TTgkEA/5FzO13w9XwhsSs/C2ZyidGoBYOzcHU4BbxNLhNPrs/46gg6p2uPwEtG1xcCWiIiIiEqlbvUwx78lAG0T1VtQuytaPQHg+41H3QJDrbRhJQnO/XL13nPxcoFqKrHMTwKy7O59hOVxeGUbD7unXAsA4+btYBEpogqAgS0RERERlZjVZsesv4qCUwHtisFZOe4BpGvLqJFUZNcodWvGOdXZ5MJUFrMJI25yH+cWKOzju/t4tvrHXP0cq82ODQfV+xIXp/WYiMoPA1siIiIiKrFDmTlu48tqVQw+cPqC2zTXllGL2YTHutTV/UyhCCatNjumLN2rOt/zfRo5Up2Hd0qEn0tA7AegX7NamP3XEbf3Sigah3ejRoEswL2qMxF5BwNbIiIiIiqxxOhQt4BRq2Kw5Fo9yjHd+W97XoHqfErKoXi0nDlf1EJsMZuQOiAZ/lc/zF+SkDowGTmX81WrMI+4qaha8/oDZzQ/486U2iXqJ0xEZYuBLRERERGVmFrAqBwqR+mMyni3ypZRoLAF9st16R4/91T2JQD6qcuu1ZMHt4nD2rHdMHvEjVg7thsGt4lTDcwBoFp4oGN91Fp0ZfO2HGMfW6IKoIq3V4CIiIiIfNvgNnHo3KA60jMvOvq0urLa7PhyfbrbdNfWUk/j2Mo2pmeheWwk3tZIQwaK+r8q18diNrn93euGWliy84TTe6cs3ovbm8d4HldXAJsPZ6FfM7baEnkTW2yJKhGrzY51BzL5ZJmIiMqcxWxC+6Rqmmm5einDM9amO/6tV8FYqXVCJLYdycLczcc05zHS/9Vqs7sFtUBRP2Ej6+Pax5iIrj0GtkSVxJy0DHSctAJDp/+JjpM47h4REV1bxUkZ9qRvci00j43Er7tP6s73SKe6Hvu/ahWGUlZU7lQ/WvP9EoBWCZGarxPRtcHAlqgSsNrsGDdvBwquPlEuENpDMRAREZUHi9nkNo6tTDlkjpFU5JGdC6sm1wgP1pxHAjC8U4Lucgr7z6o/6L29eQwsZhOsNjvW/pOpuYzHuyWxeBRRBcDAlug6oZdmPGPtIUdQK9MaioGIiKgsWG12/LTtGH7efhxWmx1Wmx0r95xSnVdZRVmrmJPSxqvDA93cpKbmPEPbxekGnHPSMtA+dQXWHVBvsQ0JLCxF4ynQ7livuv7KEtE1weJRRNeBHzYdxYs/7kKBKEydSh2QjMFt4gAU3lh89vsh1feFBPLZFhERlb05aRkYO3eHIyCUAAxpG6sZIN6REuMIQuUqy+Pm7oDWoD+tDaT+ju5eT/M1OZNJz3dpGXiiRz2EBvrrzrf92Dm0T6rmcX2IqHzxrpbIx53LhSOoBdzTjPWKdVy87HmcQCIiouKw2uxOQS1QWPl4lt6QOZuPYduRLMffg9vE4Y9x3fHoTXVV599z4jwA7Wtc/Rphuq21hzJz3DKZXMnp0TmX83XnS120h117iCoABrZEPu70JUk3zVivmuP2Y+fKdd2IiKjyMTpcj5IAcMfH65wKG1rMJgzvlKB6DRs3bwesNrtmQap/Tl1wCpRdGUl3lotH6RW9kv3moYgVEZU/BrZesGbNGtx2222IiYmBJElYsGCB2zy7d+/G7bffDrPZjPDwcNx4443IyGAVW3JXPVi4XfQlxfAGFrMJkwYmq7530mI+ZSYiorKlFwg+2D5e8zWhUthQK0hWjk/bxBKuurwVu9X78wKF18bnb2mk+TpQVFH5VPYl3fkA4FR2rsd5iKh8MbD1gpycHDRv3hwfffSR6usHDhxAp06d0KhRI6xatQrbtm3DSy+9hOBg7cp/VLm5XvSFgNOFODosUP19VweVJyIiKisWswmP3pSo+lrvphaM66sdULoWNtTKOpJbU+ekZWCX9bzqsqpHBOmuZ3Ids+7rckXlvzSGA1Iq4EC2RF7HwNYL+vTpg9dffx0DBgxQff2FF15A3759MWXKFKSkpKBu3bro168fatRQL5FPldvpS+q5VMqUrhUaVSgBDipPRERlb3gn98BWQmEwOrJzEu69MU71fcrqyEBR1pHySiddLZIIQLMAlASgR2PtiskAdItCKT+vbUKU7nIA4JNVxRuHl4jKHqsiVzAFBQX45Zdf8Nxzz6F3797YsmULEhMTMW7cONxxxx2a78vNzUVublEaTHZ2NgAgLy8PeXl55b3a5CV5eXnIuKD+mhCFF/z2iZHoXK8avv3TvWiHBKBZ7XDuIz5A3kbcVtc/buvK43re1nl5VzSn5+XlISzAPaj0k4DX+jdGdEgVp99kQAsL2idGYsuRc4AAUuKqwmIOxoaDZzULQD3bu77bclxlX7ys+ZoAcOBkNqJDqqBJrTBUDwvE6Qva8+cL4Zjf/Ttfv9uZnHEbe5ckBNtrvEmSJMyfP98RtJ44cQIWiwUhISF4/fXX0a1bNyxZsgTjx4/HypUr0aVLF9XlTJgwARMnTnSbPmvWLISEhKi8g64H53KBCZv94d7LtsjoJvmobxZ4b4cf0i9IKHoOLXBP3QK0r8lTABERla0tmRJm/uMevD5YPx8p0QLTdvth9znnxMGJLa+gqn72sJPD54F3d/oDKtdA+XP0nMsFXtms/n4JAhNa5qNqkP58avNT5XXx4kUMHToUNpsNERER3l6dSoctthVMQUHh8Cv9+/fHU089BQBo0aIF1q1bh2nTpmkGtuPGjcOYMWMcf2dnZyM2Nha9evXigeXjrLZLOHzmIuKrhcBidu5nvXbfKYjNWzXf6ycBg/p2g8UcjL59gcYTluGKY9QCCc2aJaNvqzrltu5UdvLy8rB8+XL07NkTAQEB3l4dKkfc1pXHdb2td5zAzH+2u01OaZmClNiq2LN+jdtrtRq3RvdGxrtdbTh4Fti5UfW1lJQU9E2u5XEZi8+lYcNB91oTg1vHYmj/JkWfs1n9c4DCa+3r/W/A3RrX0+t6O5MTOWOSvIOBbQUTHR2NKlWqoEmTJk7TGzdujLVr12q+LygoCEFB7o8JAwICeBL1YXPSMjBu3g4UiMLnxCNuSsTwTomOsfmSakZAgtBssX2+TyPERRdWi/x09QFFUFvopR93o1vjWrpj/VHFwmO68uC2rjyux23dNinabZoEoG3daM1KxyO/3YqBLWvjnUEtDH1GvVoRkOBeQFG6+vlGftOqJvUm1iHt4hzvr1crAn4SnNKe/QBMH9YKIYEBSIgOMXQdvR63Mznj9vUuFo+qYAIDA9GmTRvs3bvXafq+ffsQH69dIp+uP1ab3RHUAoUX7s9+P4SOk1Y4ikJZzMFoXFU71apZ7aqOZU1avMftddfqk0RERGXBYjbhie71nKZNGpgMi9mkW7Rp7uZjuuPPun6GW2Gpq58DAOsOZOoWdLLa7Fjy9wnV1y5eLnD6nNQByfCXCj/JX5KQOjAZPRrXQvukanw4TFRBsMXWCy5cuID9+/c7/j506BC2bt2KqKgoxMXF4dlnn8XgwYPRuXNnRx/bn376CatWrfLeStM1dygzR7UoRsHVcf46N6iOvLwr2HVOu8+PXFlS6+m4PFyCr7Pa7DiUmYPE6FDeYBARVRB9mlrw4Yqi+53BbQorIWec1X+gujE9C81jIw19xuA2cejcoDo2pWdBkoCW8ZFYs+80Ok5agQJReJ1LHZDs+GylQ5k5qstUuzbKn5OeedFwCy0RXVsMbL1g48aN6Natm+NvuW/ssGHDMHPmTNx5552YNm0aUlNT8eSTT6Jhw4aYO3cuOnXq5K1VJi+Qx+5TC0jlltYrV65Ar5jFmn2nMbhNHBKjQ93SqADghpgIn784K9O19W5gvIVBNxFVVv5+6tencxf1K8e2TjAW1MosZhNubV54fnXNdlI+DHY9B2u1HI/qmqR6vraYTTyPE1VgTEX2gq5du0II4fbfzJkzHfM89NBD+Oeff2C327F161b079/feytMXmExmzBCY4B7eSzA+GohUA99C42duwNWmx0WswmPdUlye33HsWzDKV8VkdYNTEUZS3BOWgY6TlqBodP/RIfUFfh09QFvrxIRUZmz2uyqab/L/rY6/S13o6kaot0PsXV8VcOttWrUsp20ut0cyVK/VjS2sOgmkS9iYEtUgakNcA/ohbLu821KLwxcq4aq30is2H2qBGtWMcxYe8jwDcy1ptZHOnXxHny6hsEtEV0/5qRloENq4QM8ZQ0Iq82O9379x2le+cFj64QozeX1aWop1fpotcKGBLrf8mqNeMmBMIl8EwNbIh+1KT0LWzLOQS8VGQCu1rpAW40bif+s3O+4EfElVpsd038/5Da9ovQb1uojPWnRngrTokxEVBryAzz5VKfMmtFrObWYTXi8q3sWEVD8NGRXOZfzVacri0EVfVaU2xVUAtCqlOtARN7BwJaoAtMqbAEUBqyeHirLhTQAoHlsJAa2rO02j6hg6btGaRXE6lQvutR9oLTS6opDq9VA2YpOROTL9IJXubaDkr8kOR48PntLI7dxZge2rF2qNGQAHj9XyWI2oX+LGKdpA1rWZj9aIh/FwJaoAvvjn0zV6RIKA9aWcVWhFd5KEjBpQLLTBfqdQS1wQ4x736GKkr5bHHJxLVdr95cuIFX2i1Wm1bnyFPxqtRoARa3oRES+Rnnu0wsiLWYT7kxxfph6R0qM0zXp43tbOf5dRYLh8Wv1yEPzyKslAXhzQFPVYNVqs2PhtuNO0xZsOe5zD3qJqBADW6IKymqz4+NV6v0xR9xU92p1xmA0i1QPbLs1rKFaHbhWRLDbND9UjPTd4tAqrlUgUOIgXa0Y1birBbiUjAS/WoE3UNSKTkTkS1zPfWv2nUbqgGTH635SURBptdkxf8sxp/e7Bo3Kc+cVgTLrFjO4TRz6NStsDb73xjjERoWoBqvFKTRFRBUfA1uiCkor1RYAqoUHOv5dy6Q+18o9p1Qv5KFB7qN8CRQODeRr+jVzLzKilXJmhFoxqgIAM9amO/622uwYa6ASs8Vswtg+jdw+g421ROSLtB78nbMXDd2jLLrkKWiUl6ckn0vLojuI2VR4nfx2Q4bmQ8jipC0TUcXHwJaogvpjv3oaMlBUgOj5uTuw7Lj6YSwAbD7s3pcz46x7v10B3+tnOyctA3dMXec2XSvlzBOtYlQA8Pnag47f5lBmjlvFTK0n/Ml1zG7TBEreokxE5C1qgWoBgNRFexx/K68lnoJGrcB3xtp0Q91BPDl38bJjnQD1DBw5bdn/av8Qf0kq8TWEiLyPgS1RBfTp6gOYulJ7WBgBYN7mo5i31Qq9NkDXAMxqs2PrEZvqvL6UfmW12TF27g7VFu3ODaq7zWvkyb9eC7kyvTkxOlR1nu3HzrlNK86wE0REFZnW+cyVsvJx6oBkR00B176uWsubvvZgqccmn5OWgV92nHCb7pqBAxSmLa8d2w2zR9yItWO7qXbhISLfwLsrogrGarMjdfEej/PtP3XB4zyuQxZsUmnBlVWUYXKM0AtClRWHjRaCAjzftMnBqMVsQnAV91PnlMV73W6+jmSp34wd1ZhORFRRZZw19uBT2So7uE0cnu7ZAADQrI7Z6cGjVoE9oxkxWtRSnJU++/2gateR9knV2FJL5OMY2BJVMHrBp0wCcKtK/1LXeVz7zWoNRg8UFpvylYv6jqPqrc4A8MR3WzAnLUO1P5jek3+9KsaA8xiI/q75dVC/+dL6vf/Yf0b3s4iIKhrJQDl3tQrEB04XPoTddtTm9IBRLVVZ7aZUQvEeumqNIa702+6ThpdHRL6DgS1RBaMXfAKFF/lJA5PRo3EtDGhhgdZwPwLA2HnO/YmOn7ukudxujUqWwnutWW12TF6i3aItj8u76XBWsapdGm2xBYAr+QVur6sVHNH6vb+7GngTEfmKdTp1H2QCzt1BrDY7FmwpGk5H+YBRrX/rY12TVJd5Ktv5XKp3fdKrSC87nZ3r8bsQke9xL49KRF7VOiFK9/UFj3dwDGA/eWAyEq4cwZGAWPywxeo2rxCFBaT6NTN5DAh7NK7p+Penaw5g0uI9EKIwRTl1QHKF6Xdk5Gl8vhDA1XVXzqtV7XJOWoZu6hpQ1GJrtdmRm+++As/1aejUSqH3e8t9dn2lhZyIKrdPVx/ArL+OGJp3U3oWbm1eeG5T6zai7IM7uE0cOjeojvTMi0iIDsHP24+7LxDA9DWH8NG9hdc9+XxdoHF9kivS63Xp6d64hqHvQkS+hS22RBWMxWzC4ypPrQFgdLckR1Ariw8Hbm9RW3N5cgOwXkA4uluSI8j6dNUBpC7a43hfSYt3lBet4k2uYqNMTuMrqqXIAe5DWKhRpsLNWKteObm2y3L1fm8OJ0FGVdTMCao8rDY7Jhmo+yBTZiyrtZ66nv+U/VvrapzfF+20OoYBMtLF5Ma6+g+Ia6iM505Evo+BLVEF9OwtjRCuMt7sM73dx0UFgPhq2kFSbFRhwKUXEMqpYFabHakqrYwVqWKyxWxCvRqeg9uLlwucnuL3blJTtdXZSAuw/LLekEBPzN7iVJxKrf8YUNjCwOEkyIgfNh0tk2FPiEpDr1ifmjqRRee2wqDVOci8IyVG8/z358GzqtPlLBdPY+PKlOnPalwrIxPR9YGBLVEFZanq/kT509XqQwBZzMHo07SW6mtp6VmO/kz9m8eoznP4arXLQ5nuY9zKKtIQNQF++usiV3ie/VdRILB010nVwMDoEBabD2fp3uAJAGMVYyRazCa8eWey23zzR3WoMGndVHGdywVe/HGXU8uU6xicRNeC0SwZmbLQntVmx3qXYHX+5mOq+7HVZsdnGg8O5XO62rqoZ8Doh+LKscmJ6PpRce5UichJ5nn34hapi/fg0zXqwW2fZPXA9vVfdjtae8b2VW/xXbjtOKw2u+4NjPJmxZusNjt2nzivO8+oq6nc4+cX9ZsVUE9Z81QN2fF+AXy9/rD+PHAebqhApRDYnR+vY8ubD7rWKcGnL0luLVNqY3ASlTeL2YR2ifqpvUrKMb0PZea4Dd+jtR9vTFdvrQWA5/s0gsVscqv0L2lkwNyRot09B3Aem5yIrh8MbIkqIKvNjrMX81Rfm7x4j+rNdXRYkOby5H5IrpUlla/LxTy0+iZVlBZbvZsfWcd61VVvqNRS1oy0RkgATIF+WLzzhOd5r6YfW212vLhgp9vrFa3PMnk2449D6JBa9inBesFyoJ96ixNbmsgb2nooaqg0aVHRNUrr/Kq2H68/oD0MWrPaVVXHp5WEcxVmWfPYSAxsqR3c+tK47URkXMW4UyUiJ3opwVpPmv0UFTseaO+e6povBNIUrYnO7y26yGuNNlRRWmyNjKUYEuinmWL8x/7TqtM1Pw+Fwysd1NkmSnL/Mr2+uxWpzzLps9rsmPjTLkdio9aDieK26M5Jy9DsP/vDpqN4b6f6/suWJvKGcJPxQTSUmSsWswmDWse6zeO6H1ttdszWqLosX5/UzqkF0D4e3hnUAj8+3gG3JruP+X5nSm3WOSC6DjGwJaqAdhy1ab6m9aR55Z5Tjn9/td69RclfktAmIVK1oJGc5mW12fHnIfUW0YrSYtsqPtLjPBcvF2imGH+86oBT8KH3EAEAPh/WCoPbxBlusThytqilQisGl9ha4DPU9g/XBxOz/jyMDleD1PapK/Dv7zbj5+3HNYNcvcquVpsdLyzYBaExEicrapM3eBhe3Y3y3HdvO/fAFnA+B+rVL0gdkAyL2aRZkE+Z+uyqeWwkXri1sdv0uRr9fInIt1WMO1UicvA03qwchDq/5xI++/2g7nIHt62DGhHBSB2Q7HRz8FCHBIzsXNgnVS/Iqygttp7Igb9Wi61rS4GnVOSQwAAAhTdIbRM8B9Xn7JcBAGv2nda+GSzmTSJ5j9r+4Yeim3KrzY4X5u902tYLtloxetYWdEhVT1vWq+y66XCW7u7R64aabGmia27e5qOG55UAtFQ8gPxpm3qFYmXXGK2gVRkgW8wmPH+Le52IKYv36gapy3epdyH5dddJzfcQkW9iYOsFa9aswW233YaYmBhIkoQFCxZozjty5EhIkoT333//mq0feZdeCuu4Po0cQajS4TMXPT5Rn/XnEXSctAIA8K8udR3TZ65Pd9x8awWDFak/kqcWVvnpvl5RqDM5uU7Vix+9KVFzXmVrwAdDUjyuX1VTIKw2O8bO3aE5jwDTSX2ZABxFbDxVyh43z72SsdZxFhLoB+HhQF6884RmATmi8rDtSBb2nrxgeP6xfYsevlptdnyuUfBso6JrjMXsPO64TLik/ifXMbvN46lrx+q96t1PMi+4F2gkIt/GwNYLcnJy0Lx5c3z00Ue68y1YsAB//vknYmLUh2ih65Pe8DNVQwJUp8dXC9FMe1WShwz5ZPVBp2nyjUPGWfWbg/Z1q3le+DWi9/v8+HjRUDqJ0aEayZzA6FlbnPo19mvm3gdLpmwNsJhNCNA5a0oAWiVEGhr3saKkdpM2uR+sK2WF7T/+ydRdhlqfWK2HLhcvF6C1gZR3rQJyROVh+hr9bCClpjERTg9f9R5EZl287PS3WhEowDlw1XsopMZqs+O3PeqBbfdGNTTXjYh8E++svKBPnz54/fXXMWDAAM15jh07htGjR+Pbb79FQIB6MEPXJ72WRq1quhZzsFMrrJ4CuPeXkm8ctAoz/XHgjGZa5bWm9fs8elNdNI8tSn+zmE3o30I7YFUG9EeytIME5U2V1WZHnk5GttxSoddHWuYrqd2VlWs/WFf5QmBTehamrvLceuqa7aD10EWvr6CSWuGdazkUEVUeVpsdP+/wXA1etvN4NrYdKWqJ1UoxBoBPVjlXRt502HNxw7eX7VOdR65t4EorsO5cP9rpekFE1wcGthVQQUEB7r//fjz77LO44YYbvL06dI3p3QjopVx1b1TT8e8RNyVoLt8PcFu+XJBGrzCTVlrltaZWlEkCMLxTgtu8sVH66dPy76mX/qks1uMpDfr25jGw2uyYtFi7j7S8vhUltZvU6XUJAAr3C82UABdqw2ypLXry4j2aN/dKyv1HWV25Q+oKfLqaacpUdjyd89QYSTEG3K9n6/arZz/c0yYOFrMJ245k4XeNDInRs7eo7vtq11MJwOS7mnn4FkTki4zXb6drZvLkyahSpQqefPJJw+/Jzc1Fbm5Rf5Hs7GwAQF5eHvLy1MdDpYopOqQKXu/f5GplVGd+ElDbHOjYpsr/v6zYzv/V6NPkJwGv928CAHjxx10oEIXTXuvfGNEhVWC1qY9zKysQwIGT2YgOKdmpw2q7hMNnLiK+Wggs5uASLSM6pArubxeHrzYUtR6PuCkB0SFV3Pb1LvWj8eEK/Rv9AD+B5rUjIME92FD+Nnl5eahjDoKfBM2A58DJbAhhoDaUBOTlXSnWsem6zal81TFrjwsNFO4XzWLCVfcbV38ezESTWmGOv/efyFadr0AAZ84be3CUl3cFGZnnnVqVBYDUxXtw7uIljLm5gaHlkHdV9OPa03GgpnmdCKfvk555XnPeAD+BvLw8WG2XMEtjuJ8u9aOQl5eHDQf00/5TF+9BfkE+RnQqqpkgX0+V17vX+zdRvV6Up4q+nanscBt7FwPbCmbTpk344IMPsHnzZkPjdcpSU1MxceJEt+nLli1DSAhbhnxNKIAJLYHVVj+stEoQkCBBYFBiAbb8sQJbXOZfvnw5/rFJAAr7H2kFXq+kXEHoye1X/w2cviSherBA6MntWLRou9My1Am8OfcvjGxS/DTa9SclzDno5/gug+sWoH3NkpUHPnjID8qEk7/3HcCiK/s15tY/za36fR3qmwUG15Xw3UE/4Gqo0s1SgK6Wot9GNihROZ+SwPe//olW0QKFv6H28SsE8P2ilahvLv73X758ebHfQ8V3LhfQ3o4CGzbvQOhJgdviJCzM0D9mLh3ZhUWLdjmWe+i8hML913nZEgR27Pgb+sdgYQD7/aKVAIAC4T7vJ6sP4dihA+hRm+W3fUVFPa7Xn1TfV7U0Mhfg2PY/cOzqKfNcLvDJZu3z4arf1+GYWeheexav3YRLBwUyzwKezq1vLd2H0NO7UVURj4dC/XrnDRV1O1PZuXiRhSG9iYFtBfP777/j1KlTiIuLc0zLz8/H008/jffffx/p6emq7xs3bhzGjBnj+Ds7OxuxsbHo1asXIiIiynu1qZwMRWErZ8bZi4iLcm/lzMvLw/Lly9GzZ09EHTmPj3Zt1F1eQFwz9G1VR/N1q+0SPt69RicFU8Ium4Tazdqj+dXqlEZaYa22S/j3O2scLVsCEr4/5I9RAzoXu+XWaruEf29Y4zTtj5P+6NiivtOTenlerHee19Wgvt1gMQejL4BROr+1TJ7vk9UHMDvtmOIVCT8f8cczgzojw3QY//3jsOZn+klFn2uUcluz3335e/Xn3QDUW5AACT9lFG7r7esPAxna27phzXA8NrgDAGBO2lFM+GkXhIBbS68kAW/0vwGhgVXwv3T9m255/wGAqbvWqLQYF61fSTMj6NqoyMe11XYJ/35b//zp6rMRXZ32uQ0HzwKbta9LySmt0L1RDVhtl/DRLvXPuu+WDmhex4yog2cxfa/+NU5AQlKLG9Eu0di449dKRd7OVLbkjEnyDga2Fcz999+Pm2++2Wla7969cf/992P48OGa7wsKCkJQkHvKUEBAAE+iPi4uOgBx0eG68wQEBMDPX7+VBwBeWLAL3RrX0hwHMy46AKkDkvG8zlA1ALDtaDZaJ0ZjTlqGIxXSTyocakeuSqx01GZzK1hVIIBjtstu381qs+NQZg5CA/2RczkfidGhTuurtiwAeHvZP7izZazbvHokAAEBVRzHiJHfWp7vthZ1XALbou/0SOckfPHHYdUUVfl3MvI5anhMlz+rzY5v/tQKagsJAGv2n8EXOg8wAGDfqQvIvHgFAPDSwl2KhztFOiZF4e1BLWAxmwz1YR/VNcmx/4zt0wipKn26BYBv/jyK8f0ae1weeV9FPK6P2mzFGnK7b3Itt/NavVr6D9aPnstFQEAAPljxt+rrA1vWRuvEaMeyPKX+SwCSakZUuN9SVhG3M5Utbl/vYvEoL7hw4QK2bt2KrVu3AgAOHTqErVu3IiMjA9WqVUPTpk2d/gsICECtWrXQsGFD7644VWgFngayReENwfP/028NUgtMXbVOiHSrGisPJaR2Y75ZpSCOWgElZSGc/lPXYej0P52G5bHa7DhzIVc1EU1tWBW9oYGA0o0nq1aURC40ZTGbMLRd0e/oJwE1wgsfPL0zqIWh35i8x8hwTQBwKjvX43zi6n45Y+0hzXkjQwM1HzapuXylaEm3t9AeDm767we9XuyNfFdidGix5l+y84Tq/qaXxNw6IRLbjmRh7uZjqq8/07vovsdiNmGEzpjjHj+MiK57DGy9YOPGjUhJSUFKSgoAYMyYMUhJScHLL7/s5TUjbyiroTr0Krgqrfkn02k4huJKjA5F89hI1aqxBQBmuBSustrsmkM0uM6nNryKPCzPp2sOoOOkFXhi9lbVAEFZvVj2y3ar7meWpjqxXO3T/2pfeH9JwpsDmjoCFOWYjM/2bogaEYWBrdZYxFRxeHogImseazZ0H33xch6m/35I8/Wft59wPLwxUoX287WFAeuctAx0SHUfZ1cmoP5QicgIi9mErg3Vx5ZVo/ZwUe8h0cCWtdE8NhJ/pZ/VXKbr8vTGHAeKHiQRUeXEVGQv6Nq1q+7wIq60+tWS7/t0zQFMWrwHwkMqrxGr9p4yPO/G9CzNMfw8jVV7+EwOrDa7YyxO1z35s98PYninBEeAt1HjpkVuLZXn0xteJV8Ix+8kk1DYL7FAuAeVQGGgrBdMFJecIq1MjR7cJg6dG1RHeuZFR0ut7Mt1RZ89ecleRF4NaHcdt6FRrfBitdDRtaU3lrTSpbwCzVRgpUOZFz227I6ftxOdG1TXPK6UCgSwKT0L4+btMNRiTFQSVpsda/adNjy/crxZmZzZ4npuH9U1Cc/d0ggAUFejZVhteZ6OTbX3EFHlwRZbIi+ZtuoAUhcVBWtyy2RJWm6ttkuYuS7d8PytE9SDWrnVVI/8VN5iNuHBDgmq8yjTndcfOKM6j2trqd74vYD7DboA8J97UjB7xI1YO7ab2wMBI+mkRlORlSnSytRooLBVo31SNadAdduRLKw74BzQZ10sHALgraX73JZBFYvRFlshgJFdkpBUXTtlUwLQJiFSd98Gisb0tJhNeK53feiFtvIYukayNFppHOtEnmg9bJQAdGkQ7Tzt6oNZ1wd2rpktspoRRTVBTIHqbSyd6kW7LU9+8KPl+T6N+NCQqBJjYEvkBVabHZOWuLfyuA5Yb9ThMxcNt8z0Ta6l2Vqr12oqU6b87j+lPj6hnO5stdkxW2NsQgE4tQbIN0DFERvlHlTKPAXKgLGn+2p9iT09gNBLrTO6DPIeIy22EoqCxugw/bE+a0QEI3VAMjyN4Lb92DkAwCOdEtE0Uv1AlLMTWsV7DliLMWIckRutIFIAWL2vaEzZoW1jsW5sd81so8Ft4rB2bDenaRMW7nI83NPqy/v7P+5ddCxmE0Z1TVKd/8H28RjZWf01IqocGNgSeYFeP7qQwOIflvHVQjwGcbL2datpvmYkGHyuT0NYzCZsO5KF3/ert8YCwIo9pzy2mo6b51xsqrhp2EeztANDOVDW+zWHtI3z+HRfLdjXegAh95fWSq0zsgzyPiMttmP7FrUMmXTml7MCBreJw4Tbb9Bd5pTFex3HQwONMY6fu6Wh4eOE/Q2pNCxmE8b2aeRxvjlpR4u9bAHPD/cEClPuXTWOUa+0nGe00AQRXbcY2BokhMCqVavw2muv4eGHH8aQIUPw5JNPYsaMGThyRH9YCCJX9stXNF+7eLmg2MuzmIM9BnGyzAu5OstRTxtTala7KgDPrZKHz1z0mDbmWmykuC2Yf+gE1kBhoPzHuO6aDwtGd6+n+/5tR7Kw/kCm6neQW9dkynTlR77cpLtcQL3YFVUMRlps5eMAAIKraAe2yu38zwn1DAeZ8mFH+nn1I2fKksLgV6vvutZnE5XEyC5JCAvSf9Bj5CGd2sNc+X2bdAqcqV2KtGqUzP4rg1kwRJUcA1sP7HY73nzzTcTGxqJPnz745ZdfcO7cOfj7+2P//v145ZVXkJiYiL59+2LDhg3eXl3yEQdL2WJbVEn5kmPa4DZxmP94B49VWrs3qqH7upw29tGQFLfXlDfKbROidJezcNtxANAdnsE1FdhIRVil79KM3cioPSwY11e/L9bT329F/6nr8OGKA6qtzsrWNavNjrGKdGVP7QZqxa6o4vD0QMY1YMw463xTLynmk7dz4di4+v2q/VB4/Fttl7D5jPoayMGAZCDPmPsYlZbVZseFXP0HPUYeoKilG0tXz/9agaoEoKVKyn1rjWuPWlVmIqpcWBXZgwYNGqBdu3aYNm0aevfurTrw8uHDhzFr1iwMHjwYL774IkaMGOGFNSVfcvSsdjDmqcV2TlqGo8+nnwQMSpTQ9+prOZfzPQZVe06c1+xjK7OYTbi1uQmjZ29xTHMNxvZ4aH2SbzIC/LUD9aTqYU433juO2jysvfpn6N28awXLtc0mrDuQ6VTlWKY3rqJMWeznUGZOsarPrh3bjQFHBSanYKpVO/aTnANGq82OXdZsp3kkAB8NTUHL+EiP1cGVCgDc+fE6DO8QD60BOeUgIiE6xGP1ZOWQU0QlYeRhY4kfoFzdeVsnRKnuy32Sa6ku12I2oU/TWli884TTdGYoEBFbbD1YvHgx/ve//+HWW29VDWoBID4+HuPGjcM///yDrl27XtsVJJ/z6eoD+HL9YdXXPF2Y1QoZzTno52i59dTSBLj3azWiQc0wp8rDVpsdz8/Vr54MFI7fOXXVAc3X/zl1wTGmrtVmxyQPw6a4Uvu9XMcF1ipM8sTsLapVjgHPadaun63aGgE4+iu7pnYzqK34RnZJwri+jRzb0A/Ao50T8YdLkRy1G/8CAFGhQU7b2UgLK1B4TH/xx2FIKiGrMqi2mE3oVE+7vzyg3j+RSI3WeOpqdRdcz2dG+nyrHSfKId/U+vIu3XlS9VpltdmxxCWoBYrqPxBR5cUWWw+aNm1qeN7AwEDUr1+/HNeGfIXamKfydL3gzdOFWa2QkYCEjLMXERcdbmjdjLRyugr093OaX69PlPP6ek4Lk8fU3Zh+1mNrs6s7UmKc1su1NTt1QLJmq5X8WXKF4s4NqjuW5SnN2vWz1cZ6FABGdUlCp/rVkRAdgvapK4r35cjrRnZOwu3NY1THKZapjdOp9sAlNtL48SYAdLcIrDohFe7LAB7pnIjhHROdWor1ircBwDn7ZcOfSZWT1WbHF2sP4b9rD6FAFD6QG9unEUZ2KawuLNddGD9vJ/KFcGTuGHmwqeTpOEmuY3Z7jzIrRmnT4SzVa0XGGaYhE1V2DGyL6dKlS9i+fTtOnTqFggLnlNHbb7/dS2tFxaUMPAGoBqF679GbTy24kp9oe6oSrCxIo0a9WqtAXFSIoeUDxgewV7Zi7jyejTlpGY7vodUnyvVz2hgYQ1NeF63xbvUs2HIcz/Ru6OjDOHbeDqdxgcfN3YH/DHXvK+zK9QaqeWwkBrasrZuOLH82AIzVuMmbuuoAIkIC0D5Jv2WNKi65dVTvdbUbf9f3GClIpdSiWgEm3NsVx2yXVYNqIymixUmPp8pnTloGxs7d4XTNEEBhCr4Ex9A5g9vEoXOD6k4PeJSBrdVm9/ig1NNxYvQBEaBfPGp093pstSWqxBjYFsOSJUvwwAMPIDMz0+01SZKQn1+8Gxfyjtl/ZWD8vMKLuZxQJeAehCrpBatKVpvd6UbBtTVQbxgRTwGnfBPiSpkUpnZz4Dpv6oBkjxd+OeVZSfk9tIp3KDWrY0aNiGCP84UEBsBqs2OWxni3ejz1cy0AsG7/Gd3fBCgq2qP0zqAW+Hm7FblX1Ps8y5/907Zjug8TJqu00CsfEpDvU7vxd6WVEq/lcoEEizlYMxND7nagt+/p9YPfdiQLf6WfRduEKI997un6I5/jtfafyYv34PbmRVkpeg94Ok5aoXlNVNI7Tow+IAI8F49iYEtUebGPbTGMHj0ad999N6xWKwoKCpz+Y1DrG6w2O16YX3QxF3BPSXXt06PWr3XcXOd+qlabHT9tO4YX5+90u1FQDoXwy3ar5rrF6Nw46N2ECEjYcuQcgKKbAy0fDkkx3B9Kb+xWi9nk8UZ9+1GboZTlkEC/YldDlikfBmitz3dpGXi+TyPdIYzkoj3KVmqrza4Z1MqfHRLo5zEgLxBwSz933X/Ie7T6FhaXxWxC+6RqmsewxWxC14bGizntPef58/QqjgPaw5/IFb/f+GUP+k9dh6e/32p4vej6oHaOV9KrMPzp6gNu83oak1amd5zIFflnj7jRqaaD2jL6NK3lNp3Fo4iIgW0xnDp1CmPGjEHNmjW9vSpUQp4u5mrj8am9pwDAh7/tB1DY+tYhdQWemL0Vv+055bZMOfiy2uz47PdDmp999JzdUUipuOv9f3O2O4KywW3i0Cahqts8/pKEVgZSgwHtgiHyTcO2I1keg9GCq08NXJfj6uLlAkNFr9SM6prk1KKgtR7NalfF2rHd8GK/xrrrq7w581TF9vk+jQynl6q1JM9Ym27ovVR+lGMPqxURK0tWmx2r97r3xday/Lgfpq/VPl8AQL9mFt3X1YKT33afcEuxn7v5mNu5p6wCfqqYjJxz1Yae06oTYWQsWyM8PSCS12Hp3yweRUTuGNgWw1133YVVq1Z5ezWoFNQCNiW1J75aNwCz/srAW0v3uPVRctWpXrQjVdaTjRpVTD2tN1BU7dhqsyMt/Zzb61oXfbm1+eftxx03sXLLr9zK6ZoWZrRqcKuESKflqPlj/2lYzCZMGqjd0qzl8hXPnQjlbWoxm9CvmUX3d1TenOlVse1QNwojOycZDsjV5vl87UEGDV6klolhtNWpJPT6v8dUVUvZl/D2sn9018fTgxXX7g1z0jLw8JebVOdVnnvKM+BnwFwxGGnxVxt6Tms/Nlq7oSxoPej1VKOCiK5/7GNbDB999BHuvvtu/P7770hOTnYb/ufJJ5/00pqRUXLAplbRUYL6eHzyDYBaa+vHKw94LNa0dn/hTZyRPnatNVpULWYTXr6tCSYs3KX5Xrl1JvPCJdXXa6sEta7FQyQAkwYW9pXS6w/lqWqwclgSeTm/bLfi9V92u8370coDuPfGeAxuE1fsSpufrz2I4Z0SNJ/Su445qrf9AecHG3pVbDccOusomDLAQ5EpAOjeqIZbaz77g3mP1WbHz9uPa6bbl8c20Tv+j59TP2Y97SN6feolybk/vdz/X4t87tEK+JVVw0vKaK0CujaqhQVpvqaV1qu1zz3fp9E1O5eprYN0DQNrIqq42GJbDLNmzcLSpUsxd+5cfPjhh3jvvfcc/73//vveXj0ySOtG6vYWMZqvDe+k/mTbSNFR5c3p412TNOfrm1xLt4hLv+QY3c+Rn5hrtTS6TnYtdAUUfp/nFf0/tdLC5KrBSp3rRzv+/dMTnZx+S7m1VMuvu07qfDMguXaE6nS9fmAAMGfkjW7bVO9GWhkE67WGyZ9rtdkxz0NQCwBdG1bXTe2ma0dujXzjF/d0yuJsk+K2PFrMJrQ12BVA5toK5vqZrpkVfgCGtovF1KEpWOcy3q7ecFoDW9Z2nHs89a8vqWvdQk76rDY7Ji/RHnquS4No1UBVbZ8b16eRo4LyteC6DgBwyw21+JCQiNhiWxwvvvgiXn31VYwdOxZ+fnwmUJaMDqVTVp+lRi+ldPx89ZYOPwl4rGsSpq48oPq6TO6rdF/7eExdpT7vyM51dZfx8o87dV+Xn5i3iodbtVRJAlrGO99U66VGPv+/7fjq4Xa6n/fOoBbIOHsRaVdTGL96uB3qjvsFBQKortISYDGb0DEpCn8ccE9j9pSm3b5uNHYcy3ab7ikQ+XrdYdSJVK9SqybjbNHNu6cWdrnolaeHGxKAm5vURGAVP0MVP6n8uA4J5eq5W4z10StOy6N8bgsN9HccK0Y1qBHmWB+tzzRSkRkADmuM8XljYiTeGdTC8feOoza3ecriIYxewMzj4NrzVLdh5b7TmsP4GN3nytPgNnE4Z89D6qLC4HzJzhOsNk9EDGyL4/Llyxg8eDCD2jJUWIhiNxZutXoccqesaAVRF3KvqE5/a8kerNyjXvTlkU518WzvRsg4cxE/6VQ8PpplR/PYSN0AbmN6lmaL7bYjWVi8071YhpI8lJDcX9X1Jtj15kNvuJA1/2Ri2xHt9ZFFhgQ6/m212eHvJ6EgX2jeMN3S1KIa2NatHqrbj2/67wdVpyv7Das9sFi43Yqftlsd6dWeTF15ABGmAIzsnIRT2erpoTJl0Su94HbETXWdUrK9eTNY2akNCaU0eckeVA0J0N1XipOqqwxGPe0navaevODYr9Uqs8uf6WmsXQDIvaKegfDnoSxHAKNVGOhfXevqVmw38lBS7UERsxa8R+0BhpLwkAZvZJ8rT1ab3WkoNYGyS5knIt/FCK0Yhg0bhjlz5nh7Na4bc9Iy0D51BX68GtQC1yY9TWss2SoqVYWsNrtmCysADO+UAAB4xEMRDvlmWq8VUKt/LWCsWNPB00VB8+A2cfhjbHfMHnEj/nBJSZRZzCZ00Rl+RKuQldKxc0XbqeOkFY4b73yN6OHmJu4VxSUAybXNbuPmKmkFBMpiITM0KsgKFBXWArRb7GWTF++B1Wb3+JuHBPrBYjZhbJ9GmvNIKNpHAGMVP6n8JEaHuqXkKxk5/xhN1XUNgIsb1Mrv2Xw4S7Mye3Eqa9/cWL2av0BROv+mw1mq6/nxygOqD56URaY6pK5wGwZGyWI2oYmlqEsBsxa8x1MaMnBti0GVRHmlzBORb2OLbTHk5+djypQpWLp0KZo1a+ZWPOrdd9/10pr5HqvNrlnAp7zT07TGks1XGbJUr4V1dLeioWZW7HYf5kfp2NUb5TX71Ft+lX3c1Hgq1gQUtnoqeXqibrXZsUpn+JGsi5d1P89qs2PX8aL04MLhfQrvNE7Y7KhdVb1/Vv8WMfhx63EARa3JOZfzddPi/FB4I+80TXHjZbXZMV1nKCVlP2cjwxSlZ17EuZw83fnkiqHJdcya8/RJZr+visRiNmFY+3jMXHdYcx5P5x+1wjVqLY+eUj2NEkL7YZyn4mlKGw5qP6iRu0oIjQdSAs4txIB64J66eA8gQbO/ZU1zEHZdPf2uHduNx4aXGNk3H+mk3UpfERg9DomocmGLbTHs2LEDKSkp8PPzw86dO7FlyxbHf1u3bvX26vkUT8GF2vh5ZUFvLNkjZ3PcxnLUG2YnNqooqPpgxX7dz528eA+2HclSDeb/O6yVUx83NWrFmlz10GiR0eJpG0xdecBjy5XWvdHd09ZrphYrb56VLdlaDWkSgDtVvvsLfRs7bryM9HWV9ylPfWf9pMJ5P9ZpqVfeQOktb8nOEyyOU8F0aVgDAFArQm2IHc8tVRazCc8rWuldK2/L1M4dfnAv4uZJq4RIzUJmnoqnyaw2e2HQqUF+SNNa5wGaawuxVnAkZzy4mpOW4dSlY+HVh1t07Wk9KFGqFh7ocR5v8jQkHRFVTgxsi2HlypWa/61YscLwctasWYPbbrsNMTExkCQJCxYscLyWl5eH559/HsnJyQgNDUVMTAweeOABHD9+fd0EeAou7pi6rkzHTpTpBXN7Tl5A/6nr8PT3Wx3T1uw7rflkW05v3WggTbhAAAu2qG9Dq02/L6fsmd4NNV8b0qZ2sS/onraBnAap936toF8rpdNqszv1RZb7RQHQTOkVAOZvca88rEzdNjKUknzz7ul3er5PI2ScvagbKBstMmQ08CDjSjsOqt/VG+ECjdZJT8OWzEnLcOrbd0eL2pqp/v93c33F5wKpA5MxyaWirB45CNbavyVoB+HK32mTznEMFD30sZhNqFdD+1hSjr2s9TBKbZ+XW3eVUhfvwadr9IvuUfnwNP4xoP2AoiIZ3CYOa8d2w+wRN2Lt2G4sHEVEDGy9IScnB82bN8dHH33k9trFixexefNmvPTSS9i8eTPmzZuHffv24fbbb/fCmnqPa7/IsqLXMiibu/kYth3JUr0ZU5Jv4LSG11GSHP/j7lR2rsf3A/pB+ZyNx8rlQcDZHO10ZLUhF5TU+jupFe+R5xvZJQlD28WqLkvt4cKWjKKbda0Ub6WjWfot1LLbm8d43KaTl+xx/N6eWr63Hztn6HPJszlpGeiQWtSnsyT7vLy/njrvftx1a1Rdd9gS1/RbAJi35Rh+2+1e2G1OWgY++PUfx9/3tYtHbFQIOjeo7rgZn/94B911FR4ejAio7/uz/jzs+J06TlqBz9eoF1+T/bK9aP2jQrRb6pRBq8VswiiN4ctcM26K27pL5cvIg0BfeSjHugVEpMQ+tgY89NBDhub74osvDM3Xp08f9OnTR/U1s9mM5cuXO0378MMP0bZtW2RkZCAu7vp4IukpGACc+0WWFblisFb/XtnG9Cw0jonw2A/p4uU8tIrX7hur1MQSrjq9eax7H021SqN66WN6lVm1aBVbUlLGd2rrJFf63ZSehSe/2+Kxv5OnflFPdK+P7/464vS6Wv9aAJj4826YAqugc4Pqug8gZNuP2nB3a4+zIT3zosdtqvy91b6T0pTFe3F78xjeeJWS67jLAsBYl36fRmhlGQDAqj3aQ5wA2gHaw19uQp+mtfDJfa0c6+oaAH+14TC+2nDYqfK7p6BOTotevku7Ivq4ee59X1+Yv9OpIN9WDxVwp/9e1Ff30hW1o815fWS5aoUJUJQdIdMKpMrjHE+eWcwm9G8egx+3aWeCVfTiUUREathia8DMmTOxcuVKnDt3DllZWZr/lRebzQZJklC1atVy+4ySKE1KoJEnxuV1YTWSrhRhquJxOASg6KZsnE5lXKDwJtxmVx9OKCTQuQjZB7/tc2ptkVullGOsqilORUi9vsZqvt1w2FH9VLlOQOFN0q3NYwz1d/LUL0rt9ef7NFJt7BZXg8tNh7MMFenperUCtF7lVjm102I24VEPla6VRYaK23JNxbcx/axbergAsKmYY8P+rDMsl4B+pWG989binSfw9tLCFGW94jzKNH39B3wCz/SqD4vZhMwL2pkTri1rRvqbu39SUbeDfJ2D6YEb450C6P+qnEPUztsWswk3N6rhNq9eKjWVHbVr9UOKau1qPKXkExFVRGyxNeBf//oXvvvuOxw8eBAPPfQQ7rvvPkRFea5SWxYuXbqEsWPHYujQoYiIiNCcLzc3F7m5Ral12dmF1Wrz8vKQl6df3bW4rLZL+HjVQczZeNQx9uzr/Zvg7lZ1DC8jOqQKIoKrIPuSerAHAM/0qo/okCrlsv6eHM68gI9Xew78mteJQF5eHsKD9J8R+UlAizoRbi17fhJQ2xzo+I5W2yW8v/wfp9aWcfN2oH1iJPI1Wke0lqVn/4lsj/MAQHiQPzIyz+PFBTtV18liLirAM6CFBe0TI5Fx9iLiokJgMQerroun+Vxfn7EuXfNGPV8I5Od77i/WMtaMzvWqISPzvG4RHQEgL+8K8vLycF+7WHy+9pBmgKL8vQe0sCApOgR3ffqn7nylIb+/rI8HX/HHP+rp5vn5+YZ/E6vtEmb9pZ++/Pnag7ivXR2nfVsWHVIFtSKCcEKj+8BHKw9gUKvaqGMO0h23Nl8IHDiZjbgo7aDu9rgCPNiuDvLy8tC5XjX85zf1AnUSnPevOuYgva+n6Up+PmZtOIS/j2ufG75cfxgNaobi7lZ1sP9Etur365gU5Xbettou4Xyu+zYSAN5avBuTByaXaJ2vB+V9XP+w6She/HGXY0xz+VqdrVP1vku9aniofVylPdeUh8p+/q5MuI29SxJa9f3JSW5uLubNm4cvvvgC69atQ79+/fDwww+jV69ehvpYapEkCfPnz8cdd9zh9lpeXh7uvvtuZGRkYNWqVbqB7YQJEzBx4kS36bNmzUJISNk9EV9/UsJ3B/3g2mFUgsCElvmoWox7quf+9EdugWpbHBLCBJ5K1g/kSuofm4SPdulVhRS4K6EA/0vXrxzZyFyAx5oU4FwuMGGzP4Rm712B2+MK0KO2wPqTEuYc9IOABAkCg+sWoH3NokNwQbqElVb3zx3dJB97zwHLj7u+JgCNZek5lwu8stkfmh1/ry57Yst8nL6k/nuNbpKP+ubyPX14Wk95v1tyVML6U1rbS+Ceq7/NWquEHzxs1wfr5yMluvB7KbcXFP+r9ntr7VfdLQXon1A++3JlcC4XOHRewsx/3M87gMCYpvmID3ee//QlCdWDC7eN/O+qQUaO/UJa+/bh88C7O/WPmwfr5+NsLrAwQ219CynPl/+3Xv358sSWVxznU+3jQKB+hMDoG5z3r4mb/XE2V3J8Vt0wgQMX9B6+CYxomI/P9+qdx5zXHVBfJ9drgdY1Q/nZrtuQyobatcl5+7nueyW7nhBRkYsXL2Lo0KGw2Wy69+1UPthia1BQUBCGDBmCIUOG4PDhw5g5cyZGjRqFvLw87Nq1C2FhYWX6eXl5eRg0aBAOHTqEFStWeDw4xo0bhzFjxjj+zs7ORmxsLHr16lVmB5bVdgn/fnuN6msCEpJa3Ih2iVFO8x8+cxHx1ULcWj+stkvIXa++LEBCRo6ElI5dVVtNSmvbURs+2uXesqb8/Ht6d8C8z/7UTXH9bETh+m04eBZi80bN+R7rkogxNzcAAPQFMMp2yam1Uma1XcK/VX4TPwnoelMHTFVpDQQk3BGfjyfv7ITYasW7MwyIO4rxC3Zpvv5IpwQM7d0QVtslTN21xql1xk8CBvXtVi7bR+mlH/8G4F4RWV6H1/vfgLtb1UE32yV01tg3AQnfH/LHqAGdsW/jUSBdv5BOSssU9G1aC4D79gKguu2Aq5kMu9c47TMSgAn3ls1+nJeXh+XLl6Nnz55uY2hfr37YdBQTr7Y2qZPQsm3Recd1frnVVG6pGtQxGh/t0tpPit6jtW+//9t+APr7T2KjGzDzJ+2sAPlTunXvXvhPjfPgK5v98dptjXBP2/jCMWhVzzES9me7nyunH96As1dbXlc/0wW/7LBi8tJ/VN5ftJzqdZtA7N3nYb2dz/VbC3Zg/lar5ut61wzlZwfHNkHfDgkeP/t6VJ7Htdq1Sbl9Xtm8zOUdkmMe+ZxZ3uf4yqIynr8rKzljkryDgW0JSJIESZIghEBBQdm3xMhB7T///IOVK1eiWrVqHt8TFBSEoCD35tKAgIAyO4ketdk0U+v8JCCpZoTjs+akZTiKp0gARtyUiOGdEh19do7a9PuvFgjgmO0y4qLL/jH+ZQObLK9AQuoA7SJTnetHO9atXi33FGOneRvUctoGcdEBqt/rmz//Uf19H+lUF5cLtNMa64YLxFYLL/Z2Hnpjom5ge1vz2ggICEBcdAD6t4jBgqvjTsr9Ystj2yhZbXZ8t1E9qAWA+aM6oHlsYZGngADtlHagaH/qdUMtTF2lHZhIANrWjdbdXlrfOy46AKkDkjF+3k7kC1Fuv1NZHtMVmdVmd6RQalGed9TmV6bPv/Tjbqwd2w0JUSakn9WvCxAQUEX1Nw72MP6nBKBauOd+iQKF+2PmBb1uERJe+Xkvbm5aG/VqRWimNgsA09ak480ByU7TZFn2K3hrmV5QW7je7epGA/Ac2Mq/+bytVregVl6WvE22HTtlqL9vZGhQpdin9Rg5ruXh5SRJQqv4SI99YOvVcn+o7S9Jju2npzyvwZVZZTl/V2bcvt7F4lEG5ebmYvbs2ejZsycaNmyIHTt24KOPPkJGRkaxW2svXLiArVu3YuvWrQCAQ4cOYevWrcjIyMCVK1dw1113YePGjfj222+Rn5+PEydO4MSJE7h8WbtPzLWgV0xJWWjCtSKoAPDZ74ecig4ZGSDedciIsuLps+XiJ4PbxCEsSH3etfuLCnFYzCY8f4t68SijBbCsNjumqxRikQAM75SgO0zRZdV07tJTVjbtUC8aAJBUPRTzRrW/JuMF6hXWubddnCOo9TQvULQdmsdGOopIuZIATBqYXKqCKRxXsfisNjt+2nYMP28/7lTcRq8Ak0x53pmh0x8aKOzXuik9y2NQK6A9zEnGGf33ju3byFCVdHl/9NSNRVk1eJJOP9RZf2U4jQl7QlFH4I6p6wwVV6sREYzHNYbvUXqkU10AhRWp1Sg/ymg3nWyNwnp6Sjuesa+Rh7p6YvZWjJ61xdBwV65DQUkS8OaApgDgsZK8WlV7IqKKjoGtAaNGjYLFYsHkyZNx66234ujRo/jhhx/Qt29f+PkV/yfcuHEjUlJSkJKSAgAYM2YMUlJS8PLLL+Po0aNYuHAhjh49ihYtWsBisTj+W7duXVl/NcOsNjsmL1FPr+tSP9pp7EetG1JlNVAjA8S7DhlRVvQ+W0LhUBwWswlz0jJwIVd9XtdKpDFV1dO17mkTZyhQ0qpkOuKmurCYTbpjRgb6lawflN4NoWtALldMPXA6B3d+vK5cxsx1pRfMj+5ez/C8QOHNuLwdHu1cV3WeD4eklEkgynEVjdO7WZeHUdJTW/EwzVOVb39J0u9SfpWk8TDKarPjh01Hdd/brHZVzx8AYFTXJFjMJo9BsPI4HNwmDmaTdpKVPCbsY99swhnF+NNGzg5yMP+sxgM6pXZ1I7HpcJbucuXK0rGRxo6B1gnGhkyTzUnL0KzSfj1yHeoKKBruSus8/tvuE24ZR0IAnRtU17xGy8ebVlV7IqKKjqnIBkybNg1xcXFITEzE6tWrsXr1atX55s2bZ2h5Xbt2hV7NropYz0uv9eT3q62X8kVQDjLUZpeHPvnjn0zdzyvPYSD0huzo1aSmY3xJvSfaroGfVstEx3qe08iBwlZktd+sWnhg0bLqR2PqKvehakraYqvXyunaAj8n7YjjtZKMmVsSFrMJY/s0Uq1ivHDbcaeHKRazCc3qmLFNJavAD4Wt3jK1zAN/SUKrYt5cU+lo3awrx2XV6w4AAE/M3oKcy1cQYiAD5I6UGEOtqY9fDTpdbUw/q/s++ZxgZIxueWgo1xY1JQkCr/e/wek41BoyDCg8Ln/ddRKLd2qPeaulOEOrpWdeRI0I/SqB8ri4Rh5g9k2u5ZR9IVMbN1uerswIulbnI2/SevApD9PUr5nz9376+62Yu1m9G8fmw1loGR+pOp74vFHtcfFygWPIMyIiX8MWWwMeeOABdOvWDVWrVoXZbNb873qmFwy6tl5azCb0aOw+ZqHsaFaOaoBW1rRS1fQu2Mt3n3TcUOml77mO8dcqPtKtMUiSgJYGbqQ/Xrkf/aeuU71xmbJ4r2P91Vqw/CQ4qr8Wl1aL2OPdktxa4F0/oTzGZlXbXrU1Wnzk1inle7erBbUSkKpIL9bKPHiuT0PeyF1jWjfryvNJ5wbqaeMyORA+d9Hz8AoLthzHqWz9ob7aJUTimd7qrZae0mo71YuGxWwy1NIcGODn8eHZsPoFTkOoGUm3P2ggqFZTnDFLE6JDdIcpAorGAzYyXvn9Nya4Tft6fbrqWN6A+kPW632saL3f0fU5+LYjWZpBLQD8sf+M5njizWMjmW1CRD6NLbYGzJw509urUOG5Pu2/qPOkfvsR/cJRgPaTaKOUxav8pML0YiNppvJNtV6rc/ukKCTXNmPbkSzkXM53tChMGpjs9pmebhCsNjumLN2r+bp8wyanI7sWJ3qtf2OEntzu8XupcV2eHwpvcEd2cU55TowOhSS530BtP3YO7ZOMtUh7MuvPw3hhwU4Il+21br96y76y7yGgHST9554U3No8xvG31gMLoymkVHa2HTmnOl2ZrWGk9bNAAJEhgR7nyxcCaelZuvO8PyRF8zVPabVrFZkrj3VJ0n14Fx8VqvvwzE8CEsOdX/QUJPZtakHd6p4DyaaWCOw+cd75mL/6IOvT1Z4fOIYEBhhqif18bWGr7YMdEjBzXbrqPGotxVab/Wo19EKuLbLygwPX1sbruT+oxWxCcu0I7DjmXm01Nsp5v/zLQ2bB7L8y8ESPehjcJg6dG1RHeuZFttAS0XWDga0BMTEx6N+/P/r374/u3bsjMNDzTdT1xsgNpqyw5e2M5utx1UI0g0alkmZkq6WqjZu7w1Cqmty/zmI2YUjbWMz664jbPOsPnMX6A0VD7ygDseLeKBj5XZU3bK6fER1SBYsWlSywVVue2jpbzCbc2y4O32xw7sc2edEe3JgYpZpGWBxWmx0vzN/pVMF2/LydaFQrXPX3B9xviLVudl3TiyvjTXFFZLXZMWWJ9gOdNftOY3CbOEMtfn4S0CohErc0rYUlOmm4/pKENgmRqg9pjMg4q98iqHzYUjVUvyrmMZsdrRLc00GBoqGJXB9YWcwm3JpcCz/vUP+OIzonokZEMF5a8Lfq67KAKn5YO7ab2zFvtdkxSSXtX6k4x4r8e+gF22otxWrnRLUHfHKKugRUiv6gtSJMqoGtay2Kqib9fU8A2JSehVubmxy/JxHR9YKpyAbMmjULISEheOKJJxAdHY27774bX3/9Nc6e1X8yej3xdIOpTAPznDInYdLAZN06LhJQ4j6Pmw5nud0sFqCooIluoRHF+xrHGBv/V1kUq7iFgzz9rmq/UVkXJzKyvIY13Yd8KABwRxkUktJKddZrXVMWgwKKWp9dU+tcv5PR+ah8abWwA4WHoPJ4iovS3jbKYm+e+tkOaBmD5rGR6JgUrTnP5sNZml0YPKUiK4O+czn6qdFTFhcG9cp90Q/Ao50T8cfY7k4pyEojNAqfdW9Y3fADpkB/9WNeb5sAhQF3cY4V+eFTrQj1wnquXR5kaudE14BamX3Ts0lNdG5Q/bqvkHz6Qq7bND+4t3ifs3tOyzdYrJqIyOewxdaArl27omvXrnjnnXfw999/Y+HChZg6dSoeeeQRtG/fHv3798ftt9+OpCTPQyVcj1yriHoaTqd1QiSax0aic4Pq2JSeBUkCjp6zY/KiPShA4cU6tYTDrsxJy9AsNvP52oPo16yWbr82uTromn2n8bKHlg8lZYtCWZLXx9uBl9bDClEGhVu0bmTbJESqtuzLQyC5MtpizhQ87/P0QEd5PCVUC0WGzhA9nRtUh9VmxzydfoUAcO5iHqw2O/44oF247o/9Z/DE7C2q42/L/ejVgj9l0Ge12fGJh5Re+ftp7Yt5eerBSQ2NILHd1S4BRjJA/kw/hzlpGW5dM9SyGZSUrdybDuundCsfOBzLUt92Wv10LWYTTIH+sF9Nd/b08Gn/qQvoMGmFWzeG64nVZsdWldR9gaLsBlnbhCjdZUkwVvuBiMgXscW2mG644QaMGzcOGzZsQEZGBu69916sWLECycnJaNq0KX755Rdvr2K50LthGnH1xk92RONGBgDq1whztCxYzCbc2jwG/ZrFYGTnJPwxrjtmj7gRf4zrXqIbE0/FWAoEkJbu3pqr5CcVjp87bt4Oj6nSru8rSTqrp0qrWkOPXEtWmx0z/kjXfL20hVu0xlpsHhvp1rIvSfpjzRptzeaQPBWbsoXupE7BJ/nBj5GArm1iVGGrpM6B/V1ahub423pjyf7wr6KxnY2MvwsUjdNdnH1R63vKxdQ8DXslk1vElVyzGVwpW9I9Ve6XUFT46/R595ZGoLDol1YLa3CVolsTT+NBH1Rs0wIBjNVZrq/SfLAI9+/rqeV+bF/jhcKIiHwNA9tSqFmzJkaMGIGffvoJmZmZeO211xAUpD8Mgq/Sq/Q5/fdDjnTUOWkZGD1ri+ZyDpy+oHnTUdpgw9MNpZ8EtLnar02NdPVpf87lfEM3pkpD2hobr9b9Mz3chlaAkZ88jVlZmqGZVB9GiKKb4sFt4rBuXHd8NCQFU4emYN3Ykj30qGi00l0rC0+BaJcG0Y7Wz70nL+jOGxLoZ6gvbkxV7YrFEoBHb0r0OP724DZxCPB3X0Dtqu79vT0pyTjdWoGrsn+vVvCtpPUwanCbOKwd2w2zR9yIIW1iNd/nqSpyAYq6pxzX2Mddq+kr+SnOi2rnVb3jRoiicbevF56qIv+2++Q1XBsiooqLgW0ZuHLlCjIzM3HnnXfi5ptv9vbqlIs1+05rtnQIAM/P3YFtR7IwVmfMSUD/Zqa0PKVAd6oXjeaxkW792oa2i3UKmozcJLvq3kh/WBItnsbVlFukvOlsjnqLi0MJ+2tZbXbM+vOwWzAhDxUiU7bs+3pLg9Vmx7i529FeYyiTysLTONYr9p52DLvlycXLBbCYTejbtJbufEK4t0rK/VrXjeuO4Z0SNQNSZSBYxc/9sunv5xyIpQ7QryFQ0oJl8tjOrpSZHUYe/OhlmFjMJiREh+C7NPfCbfL7jFRFlluktx89V+x18PPwZGDG2kO6r1fAoeBLxWI2afa7BoCXFvxt+DziOlQaEdH1hH1sy8Dff/+Nli1bIj/f88XeF8mtap7uFX7cetzjPCVN2ZXXY2P6WUiShFbxkW5BjqebLXk4Dk99LC1mk2ZfM62qpCUNPi1mE9omROIvnUJJ8g2it0SF6mchCFH8fsBz0jIwdq72PiUPFeLrgaySWv9v16FMKgOrzY6PDYxjvSk9y20oE1fK80mL2KpYpFEVWVmMTu/4V1bbVVIGov4qQdfZnFxUDy86Tga3icOeE+dVU/hLW8X39hYxSHWtXqw4kIwEOK7F11xpFZJSvs9TZfuLlwtgtdnx41ar6uv3tNHOctELa602O6b/rh3YlqbwYEXWvVF1/LDpqOprcpq4kfOI61BpRETXE7bYkkdG+4wZSZttEWsucVGoDqkr8MTsrRg9aws6pLq3dHlqaVW2FqulPSvTQ/00UoTjq6l/RmmCdb2gFihZymJZ8tSqXNyHFVabXTeoBcq3Zd8b5O+sprR9lH2Np+q7MkkC5qi0Gjpeh/NY0T9s0p53xE3uVbTVuj2otXbKfb7lea/kux+PfT743e18VFfjfPRM7walSqdXa8WWMzs81RkACi/6asXXlNTSqZXv02o5lsndE/Ra3DvW0x4DW3n+dW1d9LT/DGhZ+7oM2i5f0T9qjJ5HSvNwmYioomOLrQEtW7bUfd1uv77TeuR+XZ5uRvunxMCafQmLdcaS7NG4ZrE/Xy0QEigsPqJ8Qm0xm3BHixgs2Hpcc1larZ9z0jIcY9/qZcFNW30QwVX8cOmK883tI19uwqSBxa/G6SnVsjT9V8uKxWxCckwEdhx3H0MR0O5fLKeSJkaHFmtYEeD6G1vW03f2dqv8tWTkfCIBqBNp0hzHGAA+HJKCW5vHACjc1/45pX4saVXRVqOWoikp+nxbbXa3Yx9Qb3m/uUlNvPSjc2V1CcCAltoppUboDYdjpM6A8mEAoH6cyunU4+ftRL4QqpWJR3ZJAiRg0qI97ttSKlpXrW2tV5nXnnfF8e+Ok1Y4VTr2tP/M33wMz/RueN0Ft/HV9M+H8j7w1hL9sYjVxg4mIrpeMLA1YNeuXbjnnnuQmJio+rrVasW+ffuu8VpdOxazCR2TqmHtgTOa87RLjEKNiGAs0QlqgZLd1GkFBWopVa0TonQDW7XWT7mVQ74h1LsxzBcC+SpPztUCbSOMPjTwJqvNrhnUAkCjWu5j3Lo+KHC9MdVT2lTNisjTd/Z2q/y1ZDGbMKprEqbqpCP3S7Z47FqgTFPWqy5enBY8tb6bciEki9mk+yDKdcgvi9mEyQOT3Y6D0u7Xru93DTr1zicP3Bjv9PBN7zg1MizWyM5JqF3V5FYwUO6e0D6pGsb2aeSWOq1XM89qs8NmLwpsXR8arNl3Wj/bA4V99Mf3a6wzl++xVNXeb+ThpgDoHld3tohRHTuYiOh6wcDWgKZNm6Jdu3Z47LHHVF/funUrpk+ffo3X6topHPtRO6gFgPtvjPdYPffRm/T7dWnZcdSm+drXG9LRPqkopU2vGqZWCpbhVGsPyqPvUkUYx9ZTq/Kvu0/i/vYJjr/VHhSMnesc9OvdfAsUtZBdLyxmE8ap3OADlTM1sGP9aN0b8ITokGI9DNCrLr5gy3FDLXhWmx2fqfTdVG4fvbFe1bIMrsWYyWvHdnMKpvUeGny14TBGdk1yVJx2PU5dW50tZpPHdVYb31dZzCq5jtntPXr98tXON8o0W08FCoHrs4++Wt9u2fxRHdA8NhLrdMZoBoDndNLHiYiuB5Un/60UOnXqhL1792q+Hh4ejs6dO1/DNbq2PKVRysU69MY2NNKvS43VZlcNBmSLdpzAtiNZjnnnbTmmOW+bhCjVGx2jw3MYcfFyXrHm9/TbVoSUXE+/z+p9zsPWqD0oEACe/992x+ueniMoqyJfL0Z2SUKCSzqhaz/RysJT0PrJqoMAgMkaQ9e4Hhd6/cCN9j3UavVVFjnSGutVLVVXVt5jJrsut2P9aM15lX3X1Y7TMuvvrViuXuq0GrXq9n4o6rNr5BmkVh99Xx5mK1NjPGAA2HPiPADt4aCAwmOpsp1niKjyYYutAe+//77u60lJSVi5cuW1WRkv0EuXlQBMunrBbJ0QpbmM510Ghdfqf+lKL8WwaJ4sNI+N9DgExJ+HzuLd5XvRo1ENp0Hs5ZtVZTVUtVaZNvGRSPMwPmJxbwo9tQBVhJRctd/H1ebDWejXrHA97ZevqM6z5p9MbDuSZWg4peuxxcVqsyP9jPP+cT22ThuxUKe7AFAUYMktnpMX73FUXS/ucWH04ZBWq6/rZGUrbEigHy5eLii31tiS0DunSC6tz5LkPDROSR6kqQWbykwTT6nTSt/9lYGxKsWvBAqHnFPr9qDGNQvCarPji7WH8N+1h1TTrn3B0XPawbjc0g5U7G4tRETljS22HmRkFG+MyWPHtFsMfZXFbMIwRaqp0ufDWjluDixmE4a2jVWdr46if9CctAx0nGRsHE+9FENZQnSIxyEgZP/5bT/6T12Hp7/f6jRdeYOTVF39qXfa4SyPQ7a2LuYwE64tQP6ShHF9GmH2iBuxdmy3CnPj5emGUr45fvr7rXj4y02a863Yc8rQ511vVZEBYJPGQ5GPVuy/xmviXZ6yMADnwMRiNuH9e1Kwblx3zeNCK11e7ntoJOjUavWd/VeGWwuf3ArbPDayXFtj1bieL13/ls8pqoTzfMPaxzv+9gPw3C3FL7qkltGhFSA/dXN9zfOa1WZXDWrl1R4/byeOZBlraR11Nd0aKKqoP/33Q25p177Ucvu9TiE1+UGQXrcRX/u+REQlwcDWgzZt2mDEiBH466+/NOex2WyYPn06mjZtinnz5l3Dtbt2bkxSb40NCQxw+lvZ31VJDny0+nVpXXDl/lt6QgIDDKeoyeZuPuZIYXZ14HQO8kvw2LtdYpRTS7BRg9vEYe3Ybo6b9pFdkq75zbInv3kISE2Bfth2JAtzN+s/2IkOC/LYZxdwblm6Xmil6qsFTtczrQBfSa1yq15Kr9bwNPNHdTD8cMhiNuFhle4SFekhi9pwPmrnT60sALklVdalQQ3HvwsATFq8B2/+sqtY+6McSCt/f62HCTXCgzTPa56yc/KFADxUrZd1rFdUxVprDPaKOMyWVqr0tiNZWLb7pO57E6JDVNO4ZSX9vr6cvk1ElQ8DWw92794Ns9mMW265BTVr1kS/fv0wYsQIPPHEE7jvvvvQsmVL1KhRAzNnzsRbb72FJ554wturXC7qRoe5TVN7Kq+Vjtzqaktmcft1Wcwm9GqiP0SQXGimuN1kNyrGj/10tXYhGyW9eLd74xo6r+or7354pRUdFqj7enrmRfxlIG38Yl6+oW019jockiIuSj1Qr0iBU1lTuynW64tfNBOKdTOtlvmQOjC52A+a7r8xwW1aRRhyS2b0/KkVJLqm6J7Nuez0ugDw2e+HPGbSuBrcJg4f3NMCQOH4va6Vl2Xj5+/UXO56DwUK5VoOmq3RVym/o6fCgNuPndNd1rX0w6ajmplMRs6tp7Iv6VYSL0ma+Xd/FbZ2D53+p+rY8UREFQ0DWw+ioqLw9ttv4/jx4/jkk0/QoEEDZGZm4p9//gEA3Hvvvdi0aRP++OMP9OnTx8trW37qRBnvJ+VKGcSoBTV6F1yrzY6lu/SfVJ/KvgSL2aSZSthRoxVZThu22uyY5CE1UqZ3wExetEfzRnzbkSxM//2AZitxRdezSS3d11snRKKtTh9r2ZTFhUXYJmkUBZLdfnV80uuJ1k3n9VoVWavLQeuEKI8PNlIX7zHUVUHJNfOhuGn8Vptdvap6GRWWKwtG0361unD0bx7jdM4+ef6S6nwlSdWNDgsGAOReyXe8z7WFWU4pdl2u1WbXHbNYydN2faRTUfV9vYr6ADB5sfY5+1o6lwu8+OMuzUwmI+fWjelZmoX+ipOSL/tt9wmMVbR2CxRWpK4IvxcRkRYGtgYFBwdjwIABeO+99zB//nwsWbIE33zzDZ5++mk0bdrU26tX7l5wSX/rdUNN1RsMtTRTOf1NLhjVtWFRmpynANlI2uL0NYdgtdmxUWPe9QfPuKVo9Wlay9GaYzSN2U8qTJHUus+Vx0909fT3W9F/6jq88cse9J+6Do99o90HtaKSx+RU++4DW9ZG89hINI+NRNuEqrrLURYF0nM9tmBqFc26pWmtCtE6XZYph3pdDixmEyYNTDZ08SlugOUp80HrO8pB+Jgftrm9R1SgFnW1lmm186dWF44ftx13elBQKyJY87OKm7q6am9hd4Vj5y45HkgYbWE20j1BeR3R069Z4UM4q82OyUv0H1hWlGyJ05ck3d+peWwkBrasrbsMuYCZa4v2o50T8cfY7sV60KNVK0EA2JTumw9niahyYFVk8mjbkSzMd6liunhn4TA7rql+ahU5/SUJ24+dw72fb3C7eCvHYFRjJG1x0U4rbknWblEsEO6tZUv/PoE5aRkY3CZOt4qoTELRWIFnLuSqjncJuFfz/W33Cbd+p4t3nsDbS/fgmd6+Naagshrsxct5SM+8iNYJkU77wF2t4/BX+jnNZRhJh7teWzAtZhOaxkRg5/Fsp+lLd550BHzeMictwxGIlkXF2E2HszRv1C1mk9u+9MhXm6B1qCvfVxpfrD2E137eBQHn7+gahKsp7jBe5cnI2Ljyw4Oxc537l7qOVatXnK84x2HhGMAH3T5n3qj2qtcD1+UaqZQuv89TX1x5fGMj45NXlHPNmUvuK+r6O70zqIVuDQO53sXgNnGOCvZdG1TH+L5NirUunmolGKjnSETkNWyx9YI1a9bgtttuQ0xMDCRJwoIFC5xeF0JgwoQJiImJgclkQteuXfH33397Z2Wh3b9no8qTW4vZhJsbO/eJ7XVDTUxevEf1JmPNvtO6nx2iUwxDViCALJe+Ykbeo2xBuiNF+2m4PKSRHMAN75So3WqraAGYk5ahWSF46qoDPpnSJbeI9WhcCw/fVNftwUaMh+DjuT6FVVf1vrta4aDrRe4V93RkbxexKW5BN08+XXMAo2dtcZvueqOu3JcmqYwNq/W+krDa7Hj1alALOH9HIwFQRWjVUzLSJ39wmzh8ODTFbbpyfzuZrZ6KDDgfh3JL97YjWaot3ocyc9weTOQLgYuXCwy1MFvMJjx6U6LmukgoSqX1VCk/JLDwtsZTX/6KMob04M/+xOyDzte54qYOax0jVfyLf4v3q06RKglAS50xo4mIvI0ttl6Qk5OD5s2bY/jw4Rg4cKDb61OmTMG7776LmTNnokGDBnj99dfRs2dP7N27F+HhxsbxK0ta/XuUQ9vIN4ihgf5uF8alO0+gQGPZytYDV3PSMnTHTpX5SxIiQ/WLG6lR3uAt2KL9hHpUtySn1iuL2YQRNyWqttrKLQBq1UuV5PRGb99UlbXocP3tcOZ84QMIvRTz67F/LVB4jPxzyj3l0tutRnrposXdPz9dfUBzKB+9G3VlK+SQ6Rsc08tqLGe1VFf5O+qN0y0r7jBeFYVa3QFlEKQ15vSD7eMxsnMSAOfWfJlrq75Wpk5CdAjaJ1Xz2MIMFD4w1MqEUW4brVoKMrnFVu88Dbif173ht90nsPmIDa4duW9rbnFbN72+5lrDNP0/e/cdFsW1/gH8O/QFYWkKC9LEgkZEVOxR0VhQE40mthTjTfHGlJuY3Iipmoam+DPXdJOYakkimsRo7CUGo9ixRhEEdVFRXBSWIpzfH+sss7tTdmm7sO/nefJEZmZnz05/55zznqpqqTuvtJPaG5LzUuIdo9sEIYRIoRpbO0hJScGbb76JcePGWcxjjGHhwoV46aWXMG7cOHTu3BnffPMNSktLsXTpUjuUVrx/D9+vUqvT463fj6HvrSQxYz/KsHhIroZ08yXz2iqtTo/fDp3Hd3/nSo5pKMS/2bZmWCAx3h4uijU2n247Y1FDMa1/jGiSDr6WQ2md9VEL5Ygu6qRrgABDU22tTi/bxNzRasfqi1Q/wklJkXZ9WBSr2arNcEtySdi83V0Ux0LmayGF6mssZ7FhUPhzUKNWISJQfvu3kumL6sjMjyvhi4IVmXn4YLP4GMp8dnupZtrmtfpKfX+tqWHWqFVQuUtfxWenW5e4iK+xBQzXaSli1/XGJjWu9y8HtSaBrNKL0vl/nBANfG0dtk6r08sOK/THkQK7bzNCCJFDNbYOJicnBwUFBRg2bJhxmqenJwYOHIiMjAxMnz5d9HPl5eUoLy83/l1cbOjHV1lZicrKuvcPm3f3bZic1Br784rQLTIACa3VWPp3Dl5afczkbbrYfdSFA567ox3e3XjKYh7HAeFqD1RWVuKnfecs1qfkx8d6IaG1GlpdGfq1DcTO06bNppVqYq7rKxAZ6C3bx7aKMWRfLEawd83pEuzthjfu6oSXfjlmsqyvhwsqKyvhofDK6LlhbRHs7VbnfcN/vj72cX1I3yef2bSaAdkXi5EQ7ic630VwPDQ3rdWeosfZ8sw8dA5rgXu7t5b9fEPt68rKm5bnCDNMt+W7ThcUS55rpZXVGPNRBsZ11WC+TEbsn/adM/l76/ECxe2iZPHOHLy7wfTa48IBb4zpiGBvN+zNKUTeVfmHdfPzv6E1xL7283LDmif7QqP2Ql7hddnWME8uO4BifTkiArytvi6O66pBn5gA5F0tRWSgNzRqL5vKr9WVQV8pfbXmrx1KaReu6yuM3yu3z8Su643NXyXd1SZ1ZRb6xARAo/bC6YJi2Rel1cwQ+PPL826USd//tboynL1Siqggb+NnThcUiy4r/B57b7OmytHu1aTh0D62L7o6OZiCggIAQEiIaT/VkJAQnD17VvJzaWlpmDt3rsX0DRs2wNu7/moGQwCcvwYczQTm7HcFk6gn5cDAwIEDw4SYapw9fQKA5U2cMYatW7YAAF7b7wpbx9ZYvCYDnq7Ar3kuIp9laOPLkH1dPMrkwJB98G9c8QTi/V1wqEgqGr213HHTqbvPczD/TS+tPorKvMO4XGY5T2jv4ZMILz4uOd9WGzdurLd11da1cuCXw/L7ULjNJ7XhsPxMzX7jj5UDf22BZQ/N5mFCjOlvBgwPi/xx4++pvI763tercy2PVQbgx7Vb0U5t/Wuma+W4tR7p/Z9+8AKib+YjSqTy9lq54Zoi/Lwt20XM5vOc6LXhmdtuwufiYaxdexhbL8ifq5zE+d8Y6rqvd12s+W3FZZX4OH0r+oQwnNLJ/2bAsO2f7VwFDuLXebntcgWw+RxWLhPD/j1/Q+0BSB9nhmVMyyT+mGPP/co7eEb6NzMAX/2yFYnBzKpzq5oZztnCMn5ZYH9eEV5Zsg59QkzP410XOaw442K8R09sU40+IQybz3MwNOSTvq/be5s1dY5wryYNq7S0ebY6ayoosHVQ5gkyGGOySTNmz56NmTNnGv8uLi5GREQEhg0bBj8/8dqxuvj7zFWw/XtF57lwwI+P9UZZZRUiAw1B9aD3d0isiUNs196Gt/AS65Oz/rzcgxCHM9elt9nw20Iw5e6u0OrKcHiXVPkADhySBw82eROu1ZXhmfcsP8Nu/Z7kQG98eEx6ndsKXDDnvkEm66yNyspKbNy4EUOHDoW7u3ud1lVXv2cVAPsPyy7z3+HtMeVW88CRAGboynAg/xrAgMRI/zpvD0c3EgD75ShW7DXt080fN71ipMerbIh9rdWV4T8Sx/6g2/siobXapvWVtszB/PWWLTNqcPCK6ISRfaMt5ohdUxg4lId0QuJtoTYfG1LnKABE3dYNIzsbMql7nbiE1WcPii7nwgFvjrmtzrXGtqqPfa3VleFZk+suhx9zXDFj3AAkArLXJ8Cw7bv17I0XwnWi+1R4Lot9t3ltoDXllS+ToTy9YgJljrOaZQDcqpXWiq7t4f7RmDK8g1VlayheJy7hrx8OSs4vU0dh5MhO0OrKMGf/DtnWRy6c4Zyd8PluwdSafc7vB/68qBmf1rDMPUN7Yc3fuy3WK3R/r0hMGd3Rqt9GTDnSvZo0LL7FJLEPCmwdTGio4WGroKAAGo3GOP3SpUsWtbhCnp6e8PS0rNZwd3dvkIuon7d0kqC7E8PRIybY+Pfbvx+TbUb1d04R7usdpdhs2FYugGTSKgBYf+wSCktv4pyuXPZ7GYDzugpEBtdUM53T6UQ/wwGIDfG71V/MBfpK8RJUM8t11kVD7WdbuCpk4JzSMxIzktubTIsMdq+3bdAUaHV6/LjXMlGZC2c4bqzZh/W5r8/pdJLzKqs5m7/n8eT2CoEtENvKV3S9bUP9RJtqv73uH8z74x+bhyCSOkcB4JkVh1F2k2FiUiR8vaWrgxc/2B1DOkoPJdbQ6rKvz+l0on1jz+sq0Cc2CPPHx8s2R+avZS6u4i8PzxWVi5attkNHubuLJ7LiuQC4VlaFwtKbeDy5PW5UVOOjrdkmy7hynPE8OpRfhPSD4kEtALT0U9n9mjk8PhzBLY6h8IZ4Vv8f953Df4a2V7xHAYbtXFFtef6Y32u+333KYl3VDDh4Tr65MwBEB7ew+zZr6hzhXk0aFu1f+6LkUQ4mJiYGoaGhJs1VKioqsH37dvTt29eOJTNlPi6s0KoD540JJrQ6PRZLZKXkfbzN8HAyT6bvXW0M7yz/QMoYsP9skVXDQoiNuyjeEE7832K8lTriNjE9JLJn86KaYbIsW+UUlogeF4/0b2OXBFJiSZV4tRm79bPt2YrL8ONtmuMTEImpzRBEMcE+kknrGIDUW8mI5M7/3Weks3c7Oj5TsZAtSevGdA2DRq2SHGN26Z48fLbDdH/XZegoqeRqgOEaXA3gyaUH0G/eFqzIzMN/h8dh9sg44280T1glN2wNAKStPWFSLn5Io8ZOjtRbppUGP3yc2L40d01fqbjPpe7HLhyQFK2cgLGpZgcnhDiP5vVk3UTcuHEDBw8exMGDBwEYEkYdPHgQeXl54DgOzzzzDN5++22sWrUKR44cwUMPPQRvb29MmTLFvgUXkHsYFI7lKvUgL7b8xKRIBNVi2B4pG45eRIpCcLvx2EVo1CqkpsRJLiNWfo1ahck9I0SX33drfN9KidpaHj8sRXMi92D0zrqTTp9RU+zB0wXAtP7R9iiO/Auq/RdsWpdcVmReXYY2EsugLheIaNQqPJXcVnJ9/IstjVqFGYNiRZfhs3g3RXKZipWy7AKGDO/8eqTMMwsO5YaOUpJ1Trr1gHCVwmB5+oBY/JU6GMse7W2RQduaTNabjhmC3xWZeeh3K7M/HzjXN6nj9eL1colP1Jwv5vtSzPxb557UPj+UX4QZ3++TfLGWEBGAR2XGEgaabnZwQojzoKbIdrB3714kJycb/+b7xk6dOhVff/01XnjhBej1esyYMQNFRUXo1asXNmzYYJcxbKVo1CokRftjT+41i3nCGk65GiGecOzXKyXiTbJqo4oxhPjJZ5359dAFzEqJw/SBsfg6IxdaieFqluzMxYujTPsW9W0bjKV7LLMAc5zhIUY6ZDBobsP9KL3EqO3YqM0J/4AqbAKaNj7ebttE7vxce0QLrU5vUTZ+zOqYYB+Teda8xOKHwxKj1emRqtA0lj9nrG3u2jXSX7Y8fIbdOI34tZV/6dZUj1nh+MDCMWSVhiPjAOz457JiE2IG02uj2JjAUrXEwuMIMAxZYy3htYT/z9wdHUPwyuqjsuvZ9s9l3NEpBKkrs4xlrmaG2vy4UF+UVFRZHOe1IXW8rsjMQ2audKuAuxPDjd/N78t9uUV4atkB0ebE/Ati830+c8UBpB8Qf1HFoebFmtxYwkDTPhcIIc6BAls7GDRokOw4nhzHYc6cOZgzZ07jFcpGWp1eNKgFDA81vx68gOkDY5F3VflNPd8M8+3fj8kuJzckjxhXjkPBNflxVYUPrlUyK/9i5xlM6x9tclPnx84VforjgG5RAbLN6niXisua1UOC2EOtUHMdu7eurpXab2gAuRpbsaBOLqCUarLKmz4gBtMHxEoGxos2W/b9M3GrskqqueuA9i0tzqfNMs1ROQDdbzWtlErMV5caZkchFvgpnasM0tvUnPDaqFGr0KW1GocEta9jE8Ms1mF+HD3SP8ama7sLlPfLrweVWxxsPXEJm45dtNgOjAFjP8oAg239hMVIHa9xob6KtearD1zA88M7mIwHPDpBhfPX9Egzax0hvL4K9/mh/CLJoBYAHr29phvE97ukR15oDucCIaT5o6bIpFaUAre0dSfw2Y5s2UzOQM3bYq1OL/ummAOwakZfLHu0N94Yc5ti+Vw5Di+M6IA/jsn3s+IfBrQ6PS7JNAkTNq/madQqzBsfb2xa6sIB88YZat/+OlWoWMa9Mm/qmyK5Jt0uHEz6vzkrsSagaetO4N311tdW1Se5LgXmLyLq0n8SACb3jMJbvx8TbfKp1elFWz8IsVvnoFRz198Pa03KsiIzDz/sll5nz5hA4/F4/IJ4FssZg2Kb5TFraHEj31/S2ibEwmvj49/vMwlqAWDV/vMWfVnNj6Mv/syxaaC3asgHrtY0i+e/+7LEdV9Yg2tr/24hqeM1M7dIMZiX2gfTB5o2nTfvXyy0J/eqxTShIF9D9x+tTo+Ptkn3kbdXHgBCCLEFBbakVpRqZwBDn5+IAPkb4RPJhgdHpUC5c7gfEiIC0Cc2CHd0ks4ODQCPDYjBztRkhCt8N4eaYGuvws2fk3hbPTEp0tjH66/UwZiYFAmtTm9MiCWnOSbimD4w1iShiwsM+4PfNs5OqgnoR1uzLRLxNAaNWoWeEslrzGvalPpPKp3Dye9tw+I/c0QDY2taOPA1RlLNp9/8/bgxWLamD2lm7lVodXrZB/qOmvofKs0RGFrcyL9YE770s2a5Q/lFWHekwGJ+NQzNlXlix1E1bM+Iz788FWNNs3jAUPYhHVspLmdtkC9GKqFTUnSAYkIoQPy+Y94H+IURHSSvrz0VkvrxuQ+UknfZKw8AIYTYggJbUisatQpTJJIn8aqZIUHSYzIJKa6WGJphKmUmzjpfjEP5Rcbvni+TQZnP+ijX3Bsw3KwHtG9p+LdCzfITMjU3GrUKfWKDTPqvKT1UjYwPRUJE8wtsAZgkdPlr9mC8OLITvem/Re6FUNraE8ZjvLFodXrszhF/qbP6wAWToEas7MImoUrnsNg5wQcMSp8FgJGdNdCoVfj9sPQQLnywvO+scm1YtaAGWLLM9Tn+mANRepEgrAHcd1b+mOSXk6sZ/PzPmiRc1rwUtdb8dSdEA29rsgjzrUgSIgJwXy/5l2517UbxsGC8X07wvVJZwIWOXTCtARd7afPOH9KJ+RIiApAQIT0etTXn4Dw75gEghBBbUGBLau2pIe1k5/PJXqb1lw5s+SEjlDITA6ZNdyurpJ84+WynSsPPVKOmCR3fX1ZMSudQPD9cvmxC1jykP9A72ur1NUXmwT4x0KhVGJMQJjl/7EcZDZKRVYpcSwXzWiqNWoXgFqZZyxkMSYYAQ5/x2sSB3h4uxmb9coJ9PawaPqyKMYDJZ+gGamqApc5XYR/c5kYu8PvmX0kmGYaVXhBe0xteTirVDH645TQAw3FiDQ7Av/pF47/D20PlLl5YsS4igOFYnTVC+prNd23hf+Pt7VrKlqW23Sj4bMvCY/a/w2tqVycmRWLOaPl7i/nxXpvM0x1aySeePHz+muS8X57oS61tCCFNBgW2pNYUb/RczXIebtKHGv/WXanpsHAsvldWH5FdtqhUObuyMBmGeX9ZABgS1xK/PNEXn9zfXXFdQnJDAQHi4+IS53FHJ+mmj3zSnsYaYkaupYJY83vz85gv72c7sjH2o4xalSH/quG3Kj08f7vrLPadLVIMnl04Q0A6pqtGdrm0W/3h+XPffEs051oqucDPPKmX0gtC/vqdEBGArjI1g0v35N1qAi3f7QO4la9gfDyigrzx7vp/oK8U3+ty19L41tJlmTc+3qTFjKtC9S4fvNvCvC+x8bvMzrnBca0g1xB7T85Vi5YTtoxPrNXp8eO+c7JlfWfdSclzqzkOS0cIab4oKzKpNaWaJT7Zi0atAifz1p9/665UM7D7TBGGdAy1qqmvv8pDsbmdeTIMqaExakNqKCBCIgPlX2o05rBI3aOkayTFTscKkbGZqxjDvHUnalVbCxgCaED5elLNgCIrhgMbdCuTb2SQdJPXlM6hJoG0cCgVPrN5cw1qeVKB36H8a4gMrNl2SttBmD37jk6hOJgvPh4tfz9QqtkFDLWprfy80G/eFsVlpfABoHlg+csTfS26gfx1+rLsuuavO4G7EiyzO8uR6k9fZGMWdPPs5PyQYS+mH0EVY7KJo/hyKOFbOYhtr8Pnr6FPbJBNZSaEEHuhGltSK9YkZxGOT1su03SYf9usVDOw+FY/LWvGxu0eHSDbJFgqGUZ9NaGVa9rMIN58jjgHuSF2eI1Vo69RqzCwfbDk/H1mCYbEzmIX1L4vKgdDEKk0hi1guJ4E+HjILgMYxibV6vQYEiddM77+aIFFrbhhKJUwjOpiWwDTVEk1R/7P8oM2NYfna01XZObh/fUnJZezdrgYDkArPy/FsXYB+WspHwDyNaSuHIf5t2pqtTo9MrILjcnDvs6QHuYGkG7yLEdq+96sMj3/z14phVzDebHtNjEpEjtTk7Hs0d4mzcbFWDuWfPfoANFa/HkS/ZgJIcQRUWBLasWahw5+qAy5N8bmb5uVks/syy2yKjDgx4gVa2LIcQ3fzFDquwEaz9XZWZPYxjxhTEPq20Y6sL2mr6kh/WxHNq6Y1Zi6AHh8UKxV2V3F8GNoWtMKY3LPSHSPUs4kywchCREBGN8tXHYZZ8YHfuYPAbUZ3uZScRlmp2fJ7sO7E8MVk0wBNcGqtQmgxK6lh/KLsPjPbMSF+loEgCsy89BXMOTUkp3yfbZ53h7ij0vCIFlIo1bh7kTL4++Lnbl47seDxr9VHq6Qa4osNcyOtS9hrblfzkqJg0atEu0OxOesIISQpoCaIpNa4WtD5R5k+rVtaVxWrInTQ/2iMX1AzU3bmodbjrMuq+be3CIkRASYNDG8pq9AgLdHozUz5L97yc5cfLHzDKqZ/HiDxDlo1Cq8dXe8bIuHxX/mYEjH0EYpj4+X9G0gwNtQQ/rZ9mykiYwLWg3gk+3ZuDsxHCv3n7f5u/kxNK05pyf0aG0MxmYp1O7yQcj7E7pKlksqUHEmE5Mi4ePphieXHjCZLmwO/+4f8uPBMsCqMVlXH7iA54d3sKopMp9Q7I0xnfGSRD4FDjX9pIWe+/GgyT4f3y0c70/oCgDGlgHCMWrlxk8XEutruiIzz9iP1oUzlIevPdXq9Eg/IH7srdx/Hg/2iUJCRADOFekh9Uq3PobZkbr/Ct11K6GdVHeg5podnBDS/NCdndSKNVmM+QdH8yZhvDZmSUqseUPPB6X9FPr8CMeI5ZsY3t87utGbGWrUKrw4qqNx+BulZmPEOUxKkh8qyzxhTEOqknnibR2gglanxzyRoJZXzQxBS+82ygGLOX4MTY1ahYcVHuD5wCIuVD7Dq3DZui7jDMRqwYVj2EqN8cvjAKvGZOWD5YSIANzeTrqVAFCzb6bIDMMjdtQeyi+yeJGxcv954zBaUgmSEiP9ZcsjlkiND5LFxmU2fpdMQMhn+ZfbbHyLhrrQqFV4Mrmt7DJ864Ue0YGWLZzQfLODE0KaHwpsSa1NHxiLJ5JjJefz2U6Bmj5Bj93exjjttV+OmvTlkgqAecKpbjJPUY44RiwNf0OEDp+7Jju/MZvK3pQJbEsrqq1qSVHFGNxcDLcT/sx05TjMTomTDXiEw5Q8LDMsmLDJ6abjF2XLYk3WccpMXkOsLyrfqsSaxEMMhj6xcsPrAKb78J17ukguJ+yq8eNe+QR85k2mpZo580GkVLZ8H3f5fqiTkyItrt1i5wV/PK/IzMNTZrXg5vjfaAiqLc+w+qit5YX7y993hC+h542vaZ7uguadHZwQ0vxQU2RSJ/f3jsJHW8Xf6IvFp1/sPGP8Nz9UyIBbWUyBmua7y3bn4X+3xj0ULr//bBG6RQHbTxVKlqm5jxFLmj6lfoaN2Q+7qlq65vLw+Wu4KyFMsduBCwdjAPtQ32gMuy3UmFnc39tdtukw/zvD/MV/L8eZNjlt5esl+3uEpJIg8ePvUusJA6mM8NY0YwUMuQ/khtcBanIuAIYAKj7cD1nni02W4biaMWO1Or1ik3PzDOJSzZz5Fjz+KnfR+Uzh1c1TQyxrPMWaz7tyHLw9XBT7GwNA2a0M4xq1Fya1qcbyMzXBNccB80SaWYvR6vTIKSwxlof/t/CzSsMZCVsv1OfoAIQQ0tgosCV1IvVGn892ar6s1MDywpunRq1Cu5AWoutlTH74AmszbxJiT3L9DF0ED/eNISNb+iXRO+tOondMoOJD+ojOoVibVQAA+DojF3EaX+MQIe1biZ/LvF8PXcD0AeItP94cexuGdAwx2RZ3dArBK78clVyfMFOuXKbl1PQsk5dqzo4f09d8Wtq4eNHxWIU4DtBX3JRdP59zgXfmsuV1PHV4nPFlgzW1xeYvgBIiAjAyvuZYBAx9bPkWPFKZ9yMDffBXtvjLpvHdwiWTN5mX5e1xnVFSUaX4IgAw7bfaJ4RhxrgBOHz+uk3DTS3fk4dUQV99/gWUeX9fuREHxFoviB0LhBDSFFBTZFInUv1iU0fGWdwYbRlYXq6vj1yiGT7zJiGO7ETBdcl5yx7t3Wg1iZ9tz8a2k9KBbRVjyDQb8kfMuiM1gQTfEoNvIrpboXZ6vsxwIq/+chQ7/jEdY1SjVmFs1zDJ9fEvt6T6UxrLSdlerTIxKRLT+kk3E+cDsTMKgagwWddPe/NEs/V+v7tm2B2lhGJiifhWZObhD8Gx2LKFhzFxlBxtsXR/9tUHLljV3/3zB7thYlKkVYnQAMt+qxq1l8lwU3y25UP5RaJZl7U6vUlQC8AkKZbwHIxQGDubEEKaCwpsSZ3wb/SFZqfEidbAyPXlElu2Nn19Vh04T2PuEYemNAZ0Kz/rm9rWtRxySaEAwzkaY0ULCPMkOcK+s60lmhjz+P7EYs2GpYaeuXJDvK8kUDN0iVSGV7lyE0tanR5fSQyJ4yJoMquU7VjY3FUYfArlF+mx+bj4PHMvjOhg8gKID/SEtaWXb1Tg3fU1x7hULbDSyx1hf3ep4X0e/mafyTA+cmaLvPgVWpGZh363hiQa81GGcWgi4TmyV+GFkbDcci2RaVx1QkhzQk2RSZ1NTIo09oW6v3cUpg+UTihlS/8dqWXlmqjxD8lUa0scldIY0LUdE7Y25ZCL6/gXT6VWjINpTtglwJr+xHy/RDHm3RW0Oj3+PC0eiNzdNcz4Uo1v9SH1Gynbq3XkjpP/TUrE6FtDxfDZjv+UyH9w+Pw1Y/P0EZ1DsfnEZdHlHv5mH+aPj1esZZz3xwnc1bUmy/3zPx4SfVHx0dZs+KncMX1ArNW1qULCY1k4vI+YlfvPo2uEv+z6nkiOlWx6D9S8+DL/Dv4lD998/lpppez3CFtDcRIJGQHqvkMIaV6oxpbUK2sOKFsyBIsty4+hK/r9dJMmDk7u+AWA3w9r7VqOJ5JjTYamUnqAFtO/bbCxOeW3u85KLscBiv0SzbsryL3YekEwBJl5qw/hb6Vsr9aTOk5cOMsXA+/c00U0aSAAzFtb0+T83h6R8JRJRDw7PQs+HvKZioVNyQ/lF+Gv7CuSy/LN3TVqFRIVAk9zj/RvYzyWlfoaA0Dh9XLZ+ff3jpKdL/fiS1gLK5fwij+v+ONbKokaUPP7CCGkOaDAltSZ8Kb53d9nZW+i9YF/YLXog2uWPZUQR6Q0BvR7G07atTn9/b2jTF4m+XuLZ5KVs/O0oanmpmPyQ/PwpPrqiyXSklpWrHnnxKRI/DXbMIZ0xuzB2HXr33/NHkwZka0kdr2VutZq1CrMG2d5bQYMNef7BP21+7VrKbKUQTUzNF0eHR8qWza+hlZpCChhc/cD+ddklzU3qouhDEotLXhdIuSzQy/afFp2vtyLL+FLnkAfT8l1CIsp1/XBBfU3pBAhhDgCaopM6sT8pik2hE9D4Jsp78stwjV9BQK8PazOJEmIvU0fGAtwhlos82flxmpOL9XE1Py75TKqSuF/w6XrZbLL8deLnanJSBsXjxfTj6CKMbgAeGRADKb1i5HM1CtcdpZEv35+efOs68Q2wuutUtbeiUmRqLhZLZq5WlibW6CTrtnkW948OqAN1mSJ97kVNiW3pl96aUWlbN92Kb8fLkBCRIAx4JSLbePD/aDykH+sWronD1HB3rLH67hu4Vi5/7zJdPOcFN2jAmTLw9+HpQJyF3oRTAhphiiwJYqE4+SJDVJvzRA+DUGjVmF0At2USdM0fUAsescEYuzHGRZ9A4X9ERvKeomAobTCtOmxRq1CbLAPsq0YfoXHByalFf6Ky/LXi/rof08aji3XW7EhmYRDwGl1ehzTFot80oBPAGaeEZvnAiBN0JS84Jr8CxQAyCkslaxx5QAM6tASW09aft8XO89YXat57MJ1+Hi4KgbA89edwF0JYZLHbe82QRaB7QeTu6K7YAg9viZdaqxf/rwSG4vYBcCqGX2NwyARQkhzQU2RHdDNmzfx8ssvIyYmBiqVCm3atMHrr7+O6upq5Q/XM2F2RvOsjIBtQ/gQQkwlRAQgdYRls+R31jVsc2StTo9v/hbv+/rX6SsWy9oS1AI1NUFKtVeA6fWirv3vieOQ60euNEZtl3B/Q5ZjkaCNA7Dqib7GpuRanR4fb8uWXZ8LByRFB0gmZmMAdvxTKDqMFN/6QCnZGmAIJksrqjG5Z4Tscvw6pbiKFPTJpQcs7sETkyLh4Sr+o7hbL5fERiNIGx9PQS0hpFmiwNYBzZ8/H59++ik+/PBDHD9+HO+88w7effddLFq0qFHLYZ4sQ2zoDVuG8CGEWIpvbdknz3yIkfqmFFjUdllzSomy6HrRPIkFgcJhZaT6SfO8PVwkA0kG06GDlAJODoYXLQkRAZgl8hKJV8UY7ugYIvmiVimZFf9d0cHeiq0tXCCf5HBPjngmcfN78KH8IlRUSfx6weSJSZHYmZpskhSOEEKaI2qK7IB27dqFMWPGYNSoUQCA6OhoLFu2DHv37m3UcljbzJiaBRJSe/qKm6LTzZsE1ye5YU/GmNVaiTVl5GCoEZJq2jk7PcvYzz41JQ5pZuPlugBYNCWR+sU3U2L9UfmgD6h5ITp7ZRbE2iGVVlRL9mk1z3wvdnwKrX6ipsmt2Esk4Xq7RweY9N8Wvnix5QWP0lBThhriy6IBplanx4rMfMl1C+/BckNp8S8S+PPLvK85IYQ0RxTYOqD+/fvj008/xT///IP27dvj0KFD2LlzJxYuXCj5mfLycpSX1yTjKC429F+qrKxEZWXtHpAP5VneNF04IFztYbHOYG83BEf6Gb+TNA5+W9M2b7pOX7wuOj370nUMaFtT81Of+zrY2w1vj+2EF1cfM5k+rqsGnUJbmHxHsLcb3hzTCS//cgzVzHANeHNMJ7QP8cU9n+0WXX81A7IvFiPY2w3/6huJquoqvLP+lHH+m2M7YVjHlvX2e5qbpn5eV1aKvKzhDNP53zSuqwaxwd649/PdJn3M+XtMsLcb3hrbCS+tPmYMELlbx16wt5txPcHebvjvsHaYLzi+hK7rK4zLtlZLZxIOU3sh2NsN47pq0CcmAHlXSxEZ6A2N2guVlZVorfZU7DvLYDjue8UEWpZd8FkGw8ufPjEBCPZ2vbXNDGU8XVAs+x3Ce3Ckv3zSLHcX1mSPoeamqZ/TxHq0j+2LY0xsSHNiT4wxvPjii5g/fz5cXV1RVVWFt956C7Nnz5b8zJw5czB37lyL6UuXLoW3t+39Xa+VA3P2u4KZNCRkGBpWjdFRdMgQUl/OXgcWHHEFzM61RztUobPtCYltcq0cOHKVQ3ElcFsAQ5Sv/LKXyzi09GLw9wTW53NYe068eSYHhjndquB/K47YdZHD8jM1y05qU4U+IXQdaa5O6Th8eMzy2HiyUxXaqU33+66LHFaccQEDBw4ME9tUmxwb18qBnOuGcyPGlxmPKaEDhRy+PiV2LDLMFRyHAPDtPxz2XRFfVun+tjKHw44C6SbJ5sc9f87cqIRo+cS2x+bzHH7Nc4FYL2Xz7bM6l8NWrXR5HmpXhcRgOs8IaUylpaWYMmUKdDod/Pz87F0cp0M1tg5oxYoV+P7777F06VLcdtttOHjwIJ555hmEhYVh6tSpop+ZPXs2Zs6cafy7uLgYERERGDZsWK1OrL/PXAXbb970mcMmrSuSe3bCvd1b27xOUv8qKyuxceNGDB06FO7uto83SuxPqyvDgiM7zKZy+OKkocaKP9caa19rdWU4e6UUUUGG2iopW34+DJwTz6x8d9cwTLk73ri+Z983/X0/5rhixrgBsut3Zk39vNbqyvDx8R2mmXg5YMLIZIt9PhLADF2ZSQ2prXb9chTAeZE5HG5L6ocEQRPkoqA87FtzQnTZjRdc8coU6eOy+vAF7PjpiGQ5kju0xJS7u1lM1+rK8O374tsj2NvVuK8LS6sszhWhOXd2xJSefNKsMjyzS3pZAEhMTMRIhbGASeNo6uc0sR7fYpLYBwW2Dui///0vUlNTMWnSJABAfHw8zp49i7S0NMnA1tPTE56elq+y3d3da3URbRvqJ9pviTHglV+OI7ljKPXXcSC13c/E/s7pdKLTGYCXfzlmca415L5ekZlnTBjHAUhNicNdXcNEh/u6q2s4fjkkHtiuPqTFf1M6QqNW4ZxOZ3EdqWbAeV0FIoNlqohJkz2vI4PdRfuqSu3vyGD3Wh8LWp0eK/aKBbUG93y2G+O7heP9CV0BAEG+8oHz4fPXJcvye9Yl2c8mx4WI7i9+ewiH5nmkfwzc3d3g7m54DHN3d8c5nfSQRABwTV9lXP85nU4xaVbP2OAmefw0Z031nCbWo/1rXxTYOqDS0lK4uJgmrHZ1dW3U4X40ahUeHxiLj0SGUWiscWoJcQZSSXKAmmFBGuNcM8+CzgCkrTuBeetOgMFQw5Q2Lt6Y8GZIx1C08HTFjfIq2XKLJfehIcGav8ZKKmjNMDwr95/Hg32ikBARgAsKY95yEtmatTo9Np2QD2zv6BQiOW9iUqRJYPv5nzn4/M8cTOoRjo63bu1KibAWbjqFULUXJiZFyl43AGBct3C6RxNCnA4N9+OA7rzzTrz11lv4/fffkZubi1WrVmHBggW4++67G7Uc/j7ib504Tn6oAkKI9TRqFUZ0Fm8uaJ4BtiGJZUEHah6czYcaWZGZJxrUAqbXCBoSzHk1xljDSkMH8fbmFkGr02P+H2LNkA04DugWJT6+q1JW5Mdub1Or37l873m8tt8VP+07ZzxXpDCYnoNyAf2qA+cbdCxsQghxRFRj64AWLVqEV155BTNmzMClS5cQFhaG6dOn49VXX23UcrSRGBJkap8oeiglpJ5odXqsPyrepDdtXLzkuabV6UWbCNd2WaUaIMB0fN1UQe2TuSk9I2lIMNIo+GCQb/YspUd0gOTLG8DQdHeezPkmN0QWAPRqIx4QW4czdjsY0L6l7JL8OcgU6qkbs7UHIYQ4CgpsHZCvry8WLlwoO7yPtQp0ehy5XGHVw6+5rzPOik4ffpumzuUihBjIPWzHhYr39ftsR7ahiTCzbCJsTthvVm5ZjVqFGYPEux/w+CbE+84WyT5WPzm4rej66SGbNAThi5NfDp7HcrNxYMd3C0dCRAC0Or1kU99nh7aXPIcAw/H72O0x+PzPHNH5h/J1GNKx9oma+EBUKWC1thVHY7b2IIQQR0FNkZu5Yf+3A1MW70a/eVuwIjPP6s8dyi/Cn6cKRed5e9BhQ0h9kasJ2nzcsk/fFztzkLb2hHHsT/MmwkLm/WbllgWAOI10Ah+Og7EJ8dWScsnlnhhUuyaZhNSFRq1CdLA3Vuw1DWo5AM8P72BcRtgsXuj0pRuK3zGtf4zkvFZ+0mPkAlBsFswHon9J3Hd5j/RvY3xJ9MSgWMXlCCHEmVCE0swJH2hnr8yyus/N4h1nJOeVVjReEitCmjuNWoXhnVqJzvN0N71En70OzF9/ymI5YRNhIbHaYKllAeBaqfTA8u/e08VYo8WJjLHJ+3j7GZteohFSX3IKS2DeGpkBJsf7xKRIpM/oY5Ek6rfDFyRfDmVkF0Kr00OjVmF2Spzodw/pKJ04CgD25l6VmcswfYAhaP5YpsUEAIzqUlMr/N8RcegTKz7YtXA5QghxFhTYOpFqAEt25orOO5RfhMV/ZuNQviHBxpos8T5/1LyJkPp3Z9dw0emnL9bUIv207xwWHHGVXMdfpy9bTBNLrCOXkdjfW3qYglYtaoZJkWsuyRRqhQlpKNYe7yUVVZYBMIPFC5/PdmSjb9oWTFm8G33TtuCz7dmYPtCylnR8N8P5ywfAYnZlX5EpOYcAb3erMjz/ftj03vzU4Haiy9ELaEKIM6LA1sl8sfOMxY13xg/7MOajDLz1+wmM+SgDL/x8WPLzcslsCCG1010iE+uqgxfw2Y5saHV6vLT6GCBXU7ot2+Lc1qhVeH1MZ+PfLoLmxGJ6RIvX/gDAyYvXjf8O9JFvdilXK0xIQ7E2A7dUJuXD568Z//3Z9mxDk/9bf/PDX037eo/F59L3n0e/eVsku/1odXos25Nv8bkaDN0iA6zK8Gx+D7f15RUhhDRnFNg6mWqzt9Lv/nECa81qZ6X61hJCGgafmEbM/HUnsOn4RcWaHPNzmzc2saY2eMG9XRUT5LTwFK8V3n2mpsZJKhDn0YM1sZeJSZHYmZqMZY/2xs7UZMlEabNGWDYpfmfdSWh1emh1eqStEx8WaOsJy5YRDJDtx65UE+vrxpDQWq043A+/fuF5TsNpEUJIDQpsnYzwgVOr08tmQBVDTQwJaRijuohnG69mwOVi6WRNPA6m3QS0Oj1+O3Qer/1yxDjt2Z8OKvZ/DfT2EJ2+6cQlfLbDcL3QqFWYP178AZwerIm9WTN+bnxrtcU0vqXBvrNFdfp+8xYL/FBaUq7f5PDod/sBAAPat1SstTVP4GhNME8IIc6AhvtxIuYPnEoDzovhb9j00EpI/SqpqBKd7sIBQzq2wv+2nJb9vLBGaEVmHlJXZlnUEvH9Xwe0byl5DoskjDWav+4E7koIE/3sE4Ni0b9dSxqnljQJYtnIuVs5JPaelesPa52dpy+jT2wQAEOgPblnBJZKNkfmsO2fQhzKL0JJRZXk8F88sf6zNJwWIYRQja1TMX+Tq/QWWQw1MSSkYfh4iDcBnjEoFgkRAbjTiiynuYWl0Or0mCUS1PLk+r+uyMzD2avSLTL4ZpD8MEJCn24/Q0EtadpunTRXb0hnB+/bRrofutBHW7ONLRwAGINcOXtzixTvy+YtMwghhNSgwNaJ1McDJzUxJKRhHD6nE52+87Shz3trfy/R+Tz+gVcu+ZtwOXNiwao5/sWWrcMIEeJoxFos8UMDxbSUDhyn9IrC7JFxis2FAUMLB77rjlxiNh7/YuhRif72hBBC5FFg60TMhyKwZmgBc3GhvvVbKEIIAODS9TLR6QfzdTiUX4QB7VsqruPYBV2tk7+JBatCHGpebFEmVtLUidWM8sfw0E7SrSOeWnYA/ip3/JU6GC+P6ij7HVIJ3aR4exiG25rWP0YycDYfl5cQQkgNCmydiPlQBFJNH+Xsza1bUg1CiLg7OoZIztubW4TbNH6yn2cQz9gqtpzYg7HSUCMMwDW9oYkmZWIlTZ1GrUJyXCvj3y6wrkUSA4wtG0Z10VjdbHhv7lXZ9QrHiOfPL7EHNHqBRAgh0iiwdTLCoQikktXI6REtP8wHIaR2EiKkz60e0QGoYsrtKxIiLDO9mpNqimzNUCPCppWUiZU0dW7CNzmCfyolVuRrYjVqFe7p0dqq7+JksrJxYHhzTCeToHpiUiT+mj0Yj93exvjCiV4gEUKIPMqK7IT4vnB8UyxrmyOPjA+VffgmhNSe1DA847uFIyEiAIXFys0PWwf4IKVzKNYdKZBcRu58V2ruLHygBygTK2m6tDo9Nh67aPy7WpAxnG+9INc0f/1RLfrEBmFwh5b4ae850WX41hEatUpy7OdJSeHoWHUW93a3DJA1ahVeHNUR0/pHI7ewlJKzEUKIAqqxdVL8DXJct3CrP/NA7+iGKxAhTkwscRMH4Mup3fH+hK4AgAvX5MeP5pso/ntgG8Xvk+qjpzR+J8dRRlbSPIjlmBAOZyfVFJj3TcZZaHV6bP9Hvk+7sHmxEAdgdkoc3rjrNvh7ypfVmnF5CSGEUGDrtC4VGxLVyDWPMmc+KDwhpH6IJW5iqEkmAwBf/ZUr+XlhE8U9Cn35AKC0otIimRwAMIXmzlN6RtLDNWkWlBKg8U2BpTAA+3KLsCJTamxaA/5eK9Q/NggZswdj+sBYm8tNCCFEGkUqTmpvbhG0Oj1+3ifehEqM2KDwhJC6U3rI1ur0WH1IvHnxf4a0Nenj2tOKYUUe+XafRTI5QHlIksggqq0lzUNdE6C5cAA45a48i3fkADDtarAz+wp2/KOc6I0QQohtKLB1UvxYlNaiTIyENBylh2y5c3WSWS3qiYLrit/HV8wKk8nx5Zg/XjqB1Ly1JyxqeQlpqpQSoEllMuYApI2LR/eoAMXxbNce0eJQfpFFVwPheUcIIaR+UPIoJ+Xt4W5ToPpCSgdqgkhIA5qYFIkB7VuKJomJCfYR/Yz5M7VWp0fqyizRZaUI+xXy5fDxdMOTSw9YLMsA7D9bhFFd6FpAmge5BGhSXXXeGHubMQh+cWRHvPn7ccn1VzMgM7fIoqsBf94FR8oP40UIIcR6VGPrhPjaV41ahQ4hLaz6TJdw/4YtFCFENEmMVqeXrDkyH5NWLCGOErGEUFIZXIGa2l5CmruIAPGANz68ZlgtF4U8FS4ckBRtWbNLraAIIaT+UWDrZDgAL4ww1L5qdXqcvHjDqs9R4ihCGt8Pf59F37QteGrZQdH5LmZBKT+El01EAlWNWoXZKXEW0zkA3Wksa+IkpMZ65/NNaHV6vPn7Mdl1TEqKREJEQJ368xJCCLEORSsO6vz587j//vsRFBQEb29vdO3aFfv27avzehmA+X+cwIrMPJv62FLiKEIal1anx8urj8jWwM5KiTN5ONaoVZgn00dWjHmtL2/6wFjMHhlnDJRdAMwbH08P48RpZJ3TiU7nXyaJZTM399SQtgCU+/MSQgipO+pj64CKiorQr18/JCcnY926dWjVqhWys7Ph7+9v87rEam/4hDHpM/ooDkLPr4OaTBHSuOSaFbvAENROH2A5XMjEpEjkXS3FR1uzrfoeufN7+oBY3JUQJtrvl5DmTKvTY/4fJyymh/vX9Mnls5lL3UM5ADv+uWwMYuX68xJCCKk7qrF1QPPnz0dERASWLFmCnj17Ijo6GkOGDEFsrO1j3kk9GFcxhvyrejzcP8bY98eV4/DEIJHvsLltIyGkrsQTRjF8MKEL/pIZA1Or0+OTbdYFtYY1QnboEbF+v4Q0d1K1sW6CpyaNWoXnhnWQXAcDZT8mhJDGRIGtA/r111/Ro0cP3HvvvWjVqhUSExOxePHiev+ep5YdwOI/c8AY8NiAGOxMTUa/dsEWyzEm3lSRENJwNGoVNH6eFtNLKm5aBJlanR4Z2YXQ6vSSD+T9YoMkv2t2ehY9fBMiIDa2NACcvao3GZM274r8vZHPfkwIIaThUVNkB3TmzBl88sknmDlzJl588UXs2bMHTz/9NDw9PfHggw+Kfqa8vBzl5eXGv4uLixW/hwn+/8WfObi/ZwSkckS5uzBUVlba+EtIQ+P3Ce2b5ueLnTnQFpebTeXw8i/H0L9tMDRqLwDA4p05eHfDKWO24q6t1eBg2lqDA/BX9hXJ76pmQPbFYgR70y3BEdB5bX/B3m54c0wnvLT6mEXLp9npWegTY0iitmJvvux6XDggXO0huS9pXzsH2s/Og/axfXGM0eANjsbDwwM9evRARkaGcdrTTz+NzMxM7Nq1S/Qzc+bMwdy5cy2mRzyzAi6e4mNgmnuykyED5IfHXEXntVPToUJIY7hWDry23xVS/QD483HzeQ6/5rmILMefqxw4MAzSVGOr1vK8Fi4/t1sV/C0riAlxagcKOXx9SvyeCIjfL3kcGCa2qUafELp3EuIsSktLMWXKFOh0Ovj50TjVjY1ezzsgjUaDTp06mUzr2LEjVq5cKfmZ2bNnY+bMmca/i4uLERERgWeGtMX/dmotljev0XHhgAkjkwEAHx/fYdKUkZ/H1xARx1FZWYmNGzdi6NChcHd3t3dxSD35+8xVYP9e0XnCc/WZ93ZIrMEQ6M68IxZju4YDALZKLmtY/rakfkhorZZZhjQWOq8dR6KuDN++L35PBCzvl0ILJyZgZOdQ2fXTvnYOtJ+dhzUtJknDocDWAfXr1w8nT540mfbPP/8gKipK8jOenp7w9LSsbnlkQFv4+Kkxf90JVDPD425qShwu6PT4JuMsAMNNOm1cPCKDfQEY/v1i+hFUMWYcb4+fRxyTu7s73SybkR92izdv5MDw5pjbEBnsi4zsQtmhgADg8o1KRAb7mvQJlHLoXDF6xFj2sSf2Q+e1/UUGu+PuxHCs3H/eOO3uxHCT++XslVkwHxDPlePQs02w1fuP9rVzoP3c/NH+tS8KbB3Qs88+i759++Ltt9/GhAkTsGfPHnz++ef4/PPPa7U+8yE7tp+8jHnraoYxeH5YB5Mx9SYmRWJA+5Y0xAchdnAovwh/HLsoOu+OsGrc2701AKmsyabKKqug1emRujJLcdke0QG2FZQQJ6DV6bHqwHmTaasPXMDzwztAo1YZ75dLdubii51nUM1gfCFM905CCGlcFNg6oKSkJKxatQqzZ8/G66+/jpiYGCxcuBD33XdfrdfJj593KL8Is9OzTGp63ttwEnd3Cze5CdN4e4TYx6bj4kEtAGy84ILFO3MwI7k9NGoVpvSMwNI90slrVu4/jy4R/oo1u+O7hSMhggJbQsyJZRnnMx3z90iNWoUXR3XEtP7R9EKYEELsiAJbBzV69GiMHj26Xte5IjMPqSuzLB5yq28N50M3YkLsz8tNLskTh3fWn8Ld3SJu1RbJB7bVDLhskVm5RqsWHlg8tQcFtYRI4If9EQa3rhyH6GBvi2XphTAhhNgXjWPrJLQ6vUVNLc+Fg+hNmhDS+CKDlM/FOb8eBQCUVFTJLufCAQkR0gmhLt2owAebTtlWQEKciEatQtq4eLhyhoRs1MyYEEIcF9XYOgmx5lS8Z+5oRzdpQhxEZKByYLvh6EVodXrFfrYzBsVC5SF/md9y8jIO5RdRrS0hEijvBCGENA0U2DoJseZUvP/beAohfl4mCaQIIfahVAsLGIbqyi0sRd7VEtnl+rVtiehgb4vhvcztzaXAlhA51MyYEEIcHzVFdhJ8cyoxDEDqyixodfrGLRQhxII12Y5dOQ7eHi6y2Y75LgYatQozBsXKro8yIhNCCCGkqaPAlgAwBLf7covsXQxCnJ5GrcKgDi1ll3khpQNKKqpka2En94w01jC5yFzpB7QLptpaQgghhDR51BTZSVgzluWt3BiEEDvS6vTY8c9l2WW6hPsrNjHu0ybI+O9Wfl6S65p/T5dalJIQQgghxLFQja2T2He2SLZ2hwPQLYpqbQixN7lEb4BpE+N548W7FwBAd0Hz4js6hoguM3tkHPUbJIQQQkizQIGtk8g4XSg7v1/bIHrAJcQBxAT7QLrxBMObYzpZda6aLyO2zrsSwmwtHiGEEEKIQ6LA1glodXos25Mvu8yIzqGNVBpCiByNWoXUlDjReTM7V+He7q0BWNe9gJdTWCLaYmPJztxalpIQQgghxLFQYOsEpB5qhawZYoQQ0jimD4zF7JFxcLlVzerKcXh7bCdE+dYso9S9QEiqFviLnWcoGzohhBBCmgUKbJ0AP4atnPnrTtADLiEOZPqAWPyVOhjLHu2NnanJxppaHmPyYa3wfNaoVXj09hiLZaqZYTxcQgghhJCmjgJbJyA3hi2PHnAJcTwatQp9YsX7v/eIDpTpiwv0m7cFKzLzjH9P6x9j8YLLleMQHexdT6UlhBBCCLEfCmydxMSkSMS29JGcz2daJYQ0DUpZkasZ8GL6EWPNLf+Cy/XWuF6uHIe3x3WmpHGEEEIIaRZoHFsn4u/tLjnv7sRwesAlpImZmBQJfUUV5vx2THR+FWPILSw1ntsTkyIxoH1L5BaWGocMIoQQQghpDqjG1okU629Kzlt94AL1sSWkCQrw8TD+25qmxnLNmwkhhBBCmioKbJ2EVqfHqUs3JOfzNTuEkKZrVkocNTUmhBBCiFOipshOYt/ZItn5lESGkKbp7zNXjf+ev+4EZo2IQ5fW/tTUmBBCCCFOhWpsnYTc0CAcB6rZIaQJ0ur0WC7IfFzNgHf+OElBLSGEEEKcDgW2TqJHdKDkvC8e7I6JSZGNWBpCSH3IKSyB+Tsr6lZACCGEEGdEga2T0KhVGNct3GL6+G7hGNIx1A4lIoTUVUywD41NSwghhBACCmybhLS0NHAch2eeeabW69Dq9Fh94LzF9PahvnUoGSHEnmhsWkIIIYQQA0oe5eAyMzPx+eefo0uXLnVaT05hCapFutnOX3cCdyWE0YMwIU0UjU1LCCGEEEI1tg7txo0buO+++7B48WIEBATUaV0xwT7gRKZXM1B/PEKaOBqblhBCCCHOjgJbB/bEE09g1KhRuOOOO+q8Lo1ahdSUOIvp1B+PEEIIIYQQ0tRRU2QHtXz5cuzfvx+ZmZlWLV9eXo7y8nLj38XFxQCAyspKVFZWAgD+1TcSVdVVeG/DKVQzwIUD3hjTEcHebsZlSNPC7zfaf80f7WvnQfvaedC+dg60n50H7WP74pjcAKfELvLz89GjRw9s2LABCQkJAIBBgwaha9euWLhwoehn5syZg7lz51pMX7p0Kby9TWtkr5UDl8s4tPRi8Pes9+ITQgghhBDidEpLSzFlyhTodDr4+fnZuzhOhwJbB7R69WrcfffdcHV1NU6rqqoCx3FwcXFBeXm5yTxAvMY2IiIChYWFdGI1Y5WVldi4cSOGDh0Kd3d3exeHNCDa186D9rXzoH3tHGg/O4/i4mIEBwdTYGsn1BTZAQ0ZMgRZWVkm06ZNm4a4uDjMmjXLIqgFAE9PT3h6Wla/uru700XUCdB+dh60r50H7WvnQfvaOdB+bv5o/9oXBbYOyNfXF507dzaZ5uPjg6CgIIvphBBCCCGEEOLsKCsyIYQQQgghhJAmjWpsm4ht27bZuwiEEEIIIYQQ4pCoxpYQQgghhBBCSJNGgS0hhBBCCCGEkCaNAltCCCGEEEIIIU0a9bFtpvjhiYuLi+1cEtKQKisrUVpaiuLiYkox38zRvnYetK+dB+1r50D72Xnwz938czhpXBTYNlPXr18HAERERNi5JIQQQgghhDiP69evQ61W27sYTodj9EqhWaqursaFCxfg6+sLjuPsXRzSQIqLixEREYH8/Hz4+fnZuzikAdG+dh60r50H7WvnQPvZeTDGcP36dYSFhcHFhXp8NjaqsW2mXFxc0Lp1a3sXgzQSPz8/ulk6CdrXzoP2tfOgfe0caD87B6qptR96lUAIIYQQQgghpEmjwJYQQgghhBBCSJNGgS0hTZinpydee+01eHp62rsopIHRvnYetK+dB+1r50D7mZDGQcmjCCGEEEIIIYQ0aVRjSwghhBBCCCGkSaPAlhBCCCGEEEJIk0aBLSGEEEIIIYSQJo0CW0LsZMeOHbjzzjsRFhYGjuOwevVq47zKykrMmjUL8fHx8PHxQVhYGB588EFcuHBBcb1ZWVkYOHAgVCoVwsPD8frrr8O8K/327dvRvXt3eHl5oU2bNvj000/r++cRMx9//DFiYmLg5eWF7t27488//zTOY4xhzpw5CAsLg0qlwqBBg3D06FHFddK+dix0TjsXOqedA53XhDQhjBBiF2vXrmUvvfQSW7lyJQPAVq1aZZx37do1dscdd7AVK1awEydOsF27drFevXqx7t27y65Tp9OxkJAQNmnSJJaVlcVWrlzJfH192XvvvWdc5syZM8zb25v95z//YceOHWOLFy9m7u7u7Oeff26on+r0li9fztzd3dnixYvZsWPH2H/+8x/m4+PDzp49yxhjbN68eczX15etXLmSZWVlsYkTJzKNRsOKi4sl10n72vHQOe086Jx2HnReE9J0UGBLiAMwv1mK2bNnDwNgfHAS8/HHHzO1Ws3KysqM09LS0lhYWBirrq5mjDH2wgsvsLi4OJPPTZ8+nfXu3bv2P4DI6tmzJ/v3v/9tMi0uLo6lpqay6upqFhoayubNm2ecV1ZWxtRqNfv0008l10n72rHROd280TntnOi8JsSxUVNkQpoInU4HjuPg7+9vnPbQQw9h0KBBxr937dqFgQMHmoyVN3z4cFy4cAG5ubnGZYYNG2ay7uHDh2Pv3r2orKxsyJ/glCoqKrBv3z6LbT5s2DBkZGQgJycHBQUFJvM9PT0xcOBAZGRkGKfRvm5+6JxumuicJnLovCbEfiiwJaQJKCsrQ2pqKqZMmQI/Pz/jdI1Gg8jISOPfBQUFCAkJMfks/3dBQYHsMjdv3kRhYWFD/QSnVVhYiKqqKtFtXlBQYNwvUvN5tK+bFzqnmy46p4kUOq8JsS83exeAECKvsrISkyZNQnV1NT7++GOTeWlpaRbLcxxn8je7lYxCON2aZUj9EtvmSvtEOI32dfNB53TzQOc0EaLzmhD7oxpbQhxYZWUlJkyYgJycHGzcuNHkDbCY0NBQkxoBALh06RKAmrfBUsu4ubkhKCioHktPACA4OBiurq6i2zwkJAShoaEAIDlfCu3rponO6aaPzmlijs5rQhwDBbaEOCj+Rnnq1Cls2rTJqhtZnz59sGPHDlRUVBinbdiwAWFhYYiOjjYus3HjRpPPbdiwAT169IC7u3u9/gYCeHh4oHv37hbbfOPGjejbty9iYmIQGhpqMr+iogLbt29H3759JddL+7rpoXO6eaBzmgjReU2IA7FHxipCCGPXr19nBw4cYAcOHGAA2IIFC9iBAwfY2bNnWWVlJbvrrrtY69at2cGDB5lWqzX+V15eblxHamoqe+CBB4x/X7t2jYWEhLDJkyezrKwslp6ezvz8/ESHEHj22WfZsWPH2JdffklDCDQwfmiQL7/8kh07dow988wzzMfHh+Xm5jLGDEODqNVqlp6ezrKystjkyZMthgahfe346Jx2HnROOw86rwlpOiiwJcROtm7dygBY/Dd16lSWk5MjOg8A27p1q3EdU6dOZQMHDjRZ7+HDh9ntt9/OPD09WWhoKJszZ45x+ADetm3bWGJiIvPw8GDR0dHsk08+aYRf7Nw++ugjFhUVxTw8PFi3bt3Y9u3bjfOqq6vZa6+9xkJDQ5mnpycbMGAAy8rKMvk87WvHR+e0c6Fz2jnQeU1I08ExdqsnOiGEEEIIIYQQ0gRRH1tCCCGEEEIIIU0aBbaEEEIIIYQQQpo0CmwJIYQQQgghhDRpFNgSQgghhBBCCGnSKLAlhBBCCCGEENKkUWBLCCGEEEIIIaRJo8CWEEIIIYQQQkiTRoEtIYQQQgghhJAmjQJbQgghhBBCCCFNGgW2hBBCCCGEEEKaNApsCSGEEEIIIYQ0aRTYEkIIIYQQQghp0iiwJYQQQgghhBDSpCkGth9//DG+/vrrOn1JdHQ0HnroIZuXy83NBcdxtf5+juPw5JNPKi6XkZGBOXPm4Nq1a7X6noYmV7762D9yOI7DnDlzGmz9jmjRokVo27YtPDw8wHGccbu//PLLiIyMhJubG/z9/QEAgwYNwqBBg2z+DmvPibqoj+P6woULmDNnDg4ePFhv5XIEx44dw5w5c5Cbm2sx76GHHkJ0dHSjl6m5kdvGdTVnzhxwHFfv67WXr7/+GhzHNci2EmqM6w6pH452HVq6dCkWLlxoMZ1/Tnvvvfcav1AQf05s6OvD2rVrG+y5yNpnirfffhurV69ukDIQ223btg0cx2Hbtm31ur6ff/65XtbXmOSeGxvj3t0ogW1taTQa7Nq1C6NGjWrQ78nIyMDcuXMdOrCVKl9D759du3bhkUceabD1O5qDBw/i6aefRnJyMrZs2YJdu3bB19cXv/zyC9566y08+OCD2L59OzZt2gTAsP0//vhjm79n1apVeOWVV+q7+Cbq47i+cOEC5s6d2ywD27lz54oGEq+88gpWrVrV+IVqZuS2MTE1atQo7Nq1CxqNxt5FIUSUVGDriB555BHs2rWrwda/du1azJ07t8HWbw0KbImjkntubOhzEwDcGnTtdeTp6YnevXvbuxhOhzGGsrIyqFQqp9v+R48eBQA8+uij6Nmzp3H6kSNHAABPP/00WrVqZZzeqVOnWn1PYmJiHUrpuEpLS+Ht7W3vYtRJbGysvYvQYPR6PVQqlcX0yspKcBwHNzeHviU0Wy1btkTLli3tXYx60RyuAaRpa926NVq3bm3vYpAmQPi8S8TV5zW9Uc7N6Oho5u7uzsLCwtiMGTNYUVER40VFRTEAJv9FRUUxxhjT6/Vs5syZLCEhgfn5+bGAgADWu3dvtnr1amYuKiqKTZ061WK60nI5OTkMAFuyZInJcqtXr2bx8fHMw8ODxcTEsIULF7LXXnuNATBZDgB74okn2Lfffsvi4uKYSqViXbp0Yb/99ptxGf5z5v9t3bpVspzZ2dls4sSJTKPRMA8PD9aqVSs2ePBgduDAAZPlfvjhB9a7d2/m4+PDfHx8WEJCAvviiy+M8zds2MDuuusuFh4ezjw9PVlsbCx77LHH2OXLl60qn9z+YYwxnU7HnnvuOSbcx//5z3/YjRs3RLfTJ598wuLi4pi7uzv75JNPjPNee+0147JLlixhANiWLVvYv//9bxYUFMQCAwPZ3Xffzc6fP2+y3rKyMjZz5kwWEhLCVCoVu/3229nevXutPh7KysrY3LlzWVxcHPP09GSBgYFs0KBB7K+//jIuo9frWWpqKpM7jnnLly9nvXv3Zt7e3szHx4cNGzaM7d+/3zh/4MCBFttz6tSpotuZ3yYDBw5kAwcOtLncYtvA1v1V38e1ua1bt4qug//tU6dOZT4+Puzw4cNs6NChrEWLFqx3796Sv09se/HfsXTpUvbiiy8yjUbDfH192ZAhQ9iJEycsPr9u3To2ePBg5ufnx1QqFYuLi2Nvv/22cX5mZiabOHEii4qKYl5eXiwqKopNmjSJ5ebmGpfhj2Hz//jrDL/Phaw9zqKiotioUaPYunXrWGJiIvPy8mIdOnRgX375pVXbvD6Peb4sK1euZF27dmWenp5s1qxZxm3+7bffspkzZ7KwsDDGcRw7fvw4Y4yxjRs3ssGDBzNfX1+mUqlY37592aZNmyzKevz4cTZp0iTWqlUr5uHhwSIiItgDDzzAysrKFLexLd+zZs0alpCQwDw8PFh0dDR79913Ra/31rLlGlZVVcXmz5/POnTowDw8PFjLli3ZAw88wPLz802WGzhwILvttttYRkYG69Onj/HY++qrr4y/ITExkalUKta5c2e2bt060TLl5ORYrHPPnj2sf//+TKVSsZiYGJaWlsaqqqqMyzXEvbioqIj961//YgEBAczHx4eNHDmSZWdnW9wP+P2wb98+Nn78eObv789CQ0MZY4xVV1ezjz76iCUkJDAvLy/m7+/Pxo8fz7Kzsy2+z5pjgf+uI0eOsEmTJjE/Pz/WqlUrNm3aNHbt2jXZ3/Phhx8yjuPYxYsXjdPee+89BoDNmDHDOK2qqor5+/uzmTNnGqfNmTOH9ezZkwUEBDBfX1+WmJjIvvjiC1ZdXW1cZsyYMSwyMtJkv/B69uzJEhMTjX9bu13ErkPWftbaY4cxxo4cOcKGDh3KVCoVCw4OZjNmzGBr1qwxuV+I3Rv5849/Tnv33XfZ+++/z6Kjo5mPjw/r3bs327Vrl9xuYYwxdunSJfb444+zjh07Mh8fH9ayZUuWnJzMduzYYbHs+fPn2b333statGjB/Pz82IQJE9iuXbssri1Sz4PCY5dnfk6UlJQY78Oenp4sICCAde/enS1dupQxZtgvYtuCP3et3UfV1dVs/vz5LDIyknl6erLExES2du1a0WcKc2LfL/xMVlYWu+uuu5i/vz/z9PRkCQkJ7Ouvv5Zdp3DdTzzxBPvqq69Y+/btmZeXF+vevTvbtWsXq66uZu+8845xHycnJ7NTp05ZrMOa8/nUqVPsoYceYm3btmUqlYqFhYWx0aNHs8OHD5ssV1VVxd544w1jWdRqNYuPj2cLFy40LiN2rjAmfRxIPe/+888/bPLkyaxly5bMw8ODxcXFsQ8//NBivcePH2fDhw9nKpWKBQUFsenTp7Nff/3Vqmcsa383f5/+7rvv2LPPPstCQkKYl5cXGzBggMlzK2PWxSS23su2b9/O+vTpw1QqFZs4cSJjrOaZIj09ncXHxzNPT08WExPDPvjgA4tySz03iu0TW8umdF3DK6+8wjZs2MDee+895uPjwxITE1lZWRljjLH9+/ezNm3asMTERLZr1y62a9cu4wa9du0ae+ihh9h3333HtmzZwv744w/2/PPPMxcXF/bNN9+YFKY+A9t169YxFxcXNmjQILZq1Sr2008/sV69erHo6GjRAzg6Opr17NmT/fjjj2zt2rVs0KBBzM3NzXiRyc/PZ0899RQDwNLT042/U6fTSZazQ4cOrG3btuy7775j27dvZytXrmTPPfecyQH9yiuvMABs3Lhx7KeffmIbNmxgCxYsYK+88opxmU8++YSlpaWxX3/9lW3fvp198803LCEhgXXo0IFVVFQolk9u/5SUlLCuXbuy4OBgtmDBArZp0yb2wQcfMLVazQYPHmxyUwbAwsPDWZcuXdjSpUvZli1b2JEjR4zzxALbNm3asKeeeoqtX7+effHFFywgIIAlJyebbKfJkyczFxcXlpqayjZs2MAWLlzIIiIimFqtVjweKisrWXJyMnNzc2PPP/88W7t2Lfv111/Ziy++yJYtW8YYM9wYhg8fztzc3JjcccwYY2+99RbjOI7961//YmvWrGHp6emsT58+zMfHhx09epQxxtjRo0fZyy+/bDzmdu3axU6fPs3279/PHn74YQaA/fHHH2zXrl3GE878JmRNuRkTv5nasr8a4rg2p9PpjPv75ZdfNq6D/+1Tp05l7u7uLDo6mqWlpbHNmzez9evXi/4+nlRgGx0dze677z72+++/s2XLlrHIyEjWrl07dvPmTeOyX3zxBeM4jg0aNIgtXbqUbdq0iX388ccmD6Y//fQTe/XVV9mqVavY9u3b2fLly9nAgQNZy5YtjS+MLl26xN5++20GgH300UfG33Xp0iXj7xLeJG05zqKioljr1q1Zp06d2LfffsvWr1/P7r33XgaAbd++XXZ71/cxHxUVxTQaDWvTpg376quv2NatW9mePXuM2zw8PJzdc8897Ndff2Vr1qxhV65cYd999x3jOI6NHTuWpaens99++42NHj2aubq6mjyYHDx4kLVo0YJFR0ezTz/9lG3evJl9//33bMKECay4uFhxG1v7PZs2bWKurq6sf//+LD09nf30008sKSmJRUZG1jmwteYa9thjjzEA7Mknn2R//PEH+/TTT1nLli1ZRESEyQvIgQMHsqCgIONLjPXr17PRo0czAGzu3LksPj6eLVu2jK1du5b17t2beXp6mgTRUoFtUFAQa9euHfv000/Zxo0b2YwZMxgAk3tsfd+Lq6qqWP/+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358NHDiQnTx50mltq4Qrzy2OmfPmzWONGzdm3t7erFmzZmzx4sWycrm5ufZ6+/j4sKCgINa+fXu2aNEiWblVq1axzp07M4PBwGrXrs369evHkpOTZWXE9rdv3z42cuRIFhgYyMLDwxljxeuzo0ePsj59+jA/Pz8WEhLCJk+ezHJzc2X1dvwn9j2lb+Jq3fSMa47Mnj2bCYLALl++bD/24YcfMpw+fZo99NBDDADbuXOn/V9eXh4rKipiAwcOZLVq1WJvvvkmW79+Pfvwww+Zv78/a9u2rb1D7d+/nzVs2JC1bdvW/ntREMjJyWFPPvkk++9//8s2b97MfvvtN/bqq68yDw8P9u2338oq6U7Bdu3atczDw4P16tWLrVixgi1dupR16tSJxcTEKAq2MTExrGPHjuynn35ia9asYb169WK1atViaWlpjDHGMjMz2fPPP88AsOXLl9uf02KxcOvZtGlT1qhRI/bf//6Xbdu2jS1btoy98sorso//5ptvMgBsxIgRbOnSpWz9+vXs448/Zm+++aa9zOeff86SkpLY6tWr2bZt29i3337LWrduzZo2bcry8/M166f2fXJzc1mbNm1YSEgI+/jjj9nGjRvZp59+yoxGI+vTp49sEgLA6tevz1q1asUWLVrENm/ezI4ePWo/pyTYNmzYkD3//PNs3bp17Msvv2RBQUGsd+/esvc0ZswY5uHhwaZOncrWr1/PPvnkExYZGcmMRqNmeygoKGC9e/dmtWrVYq+++ipbs2YNW716NXv99dftA4XedswYY++++y4TBIE99dRT7JdffmHLly9nXbp0Yf7+/uzYsWOMMcaOHTvG3njjDXub27lzJzt9+jTbv38/++tf/8oAsN9++43t3LnTvrB1nID11Jsx57bu6vcqi3btiMVisX/vN954w34N8dnHjRvHvLy8WExMDEtKSmKbNm1i69atU3w+EZ5gGxMTwx599FH266+/ssWLF7OoqCjWuHFjdvfuXXvZL7/8kgmCwHr16sUWLVrENm7cyObOncsmTZpkL7N06VL2z3/+k61YsYJt27aN/fjjj6xnz54sNDTUvli9cuUKe++99xgANmfOHPtzXblyxf5c0sWTK+0sOjqaNWjQgDVv3px99913bN26dezhhx9mANi2bdtU37e723x0dDQzmUysYcOG7Ouvv2Zbtmxhe/bssb/z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VomeRdqneFgsFvzyyy946623MHXqVPvxO3fucBegenDHeKkHd7Z5EbV+7XhO/C6fffYZNxJivXr1UFhYCE9PT9VvoYbe+4iRmpXG9vIY78UxzGw2OwnxFy9erDR9sqzmYsf2ERQUBE9PTzz++ONca6TY2FgAtndXmu/WoEEDNGnSBBs3bkRMTAw6dOiAwMBA9O3bF5MmTcLu3buxa9cuWTrDzZs34+LFi9i6datdSwtAMeWN+Bz/+c9/kJOTg0WLFuHOnTsYP368vYzedqpEaX7LQ+z7169flwkt5bn2+fHHH+Hl5YVffvlFpr10TBUi9p1Lly6VaP739fWFxWJxOq60CT1+/HiMHz8eubm52L59O9566y0MGzYMf/75p+p8XxnGobp162L37t1gjMn625UrV3D37t0yHWNc6c+ujDEPPPAAHnjgAdy5cwe7du1CUlISxo4di5iYGHTp0sXebhx/y1MwKM1hetdLYp+5du2aTMDS+91cHVvV2oh4f39/f8yYMQMzZszA5cuX7drbv/zlLzhx4oRs3nHk4sWL8PDwcLKu5M3zas9ekg2mktStpPTt2xeAbQNzw4YN6N+/v01jy5OMxR98//33suPLli1Dbm6u/bx4DSXJWhAEeHt7y17opUuXsGrVKnc8kyL+/v7o0KEDVq5cifz8fPvxW7du4ZdffinxdfXuICjRpEkTvPHGG0hISMD+/fsBAAMGDICnpyc+//xz7u/E9ybeW2T+/Pku1Y/3fYYNG4a0tDTUrVsXHTp0cPoXExOj+xlLQo8ePQAAS5YskR3/3//+h7t372r+fvDgwcjLy1PN0au3HQ8cOBC1atVCWlqa4rvo0KGDK49W6norURbfqzTturTXiImJweHDh2XH/vzzT5w8ebJE9ejatSuMRiPmzZsHxphiGUEQwBhz6lNffvklCgsLZcdceS5XxsvS4M42XxK6deuGwMBApKamcvuJt7c3DAYDevbsiaVLl6paO/Desd77+Pv7o2PHjli+fLlsd/zmzZv4+eefS/ycehHN3xzfdUpKCo4fP+62715aymsu9vPzQ+/evXHgwAG0atVK8buJC5/evXvj2LFjOHTokOwaixYt0n2/fv36YfPmzfZFDWCbb6OiovDPf/4TBQUFds0u4NqcCtgEory8PCxevBjffPMNunTpgvj4ePt5ve1UidL8loco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szOllNItt9ySpk6d2u15X//619NnP/vZv/0E6NeMGTPS9ddf323b1KlT05IlS1JnZ2eqq6tLd955Z37f3r17U3V1dbrvvvv6LFNbFzd9+sSmT5cm/RqKm6nIkBGtra2Ry+Vi5MiR+W0LFy6M2bNn5x+/+OKLMWvWrG6LwM+bNy927NgRTU1N+WPmzp3brex58+bFq6++Gh0dHQN5CiVp3759sX79+h6/87lz58YLL7wQW7dujebm5m77y8vLY9asWfHCCy/kt2nrE48+nU36NP3Rr6FwBFvIgL1798aSJUvimmuuiaqqqvz2MWPGxPjx4/OPm5ubo7a2tttzux43Nzf3e8z+/fujpaVloE6hZLW0tMSBAwd6/Z03Nzfn26Wv/V209YlFn84ufZq+6NdQWCcVugJA/zo6OuLqq6+Ozs7OWL58ebd9y5Yt63F8Lpfr9jj99WYUB28/kmM4tnr7nR+uTQ7epq1PHPr0iUGf5mD6NRSeEVsoYh0dHXHllVfG1q1bY9WqVd2+Ae5NXV1dtxGBiIh33303Ij7+NrivY0466aQYNWrUMaw9ERGjR4+OwYMH9/o7r62tjbq6uoiIPvf3RVtnkz6dffo0h9KvoTgItlCkut4oN2/eHM8888wRvZHNnDkzGhoaYt++ffltTz/9dNTX18eECRPyx6xatarb855++uk477zzoqys7JieAxFDhgyJ6dOn9/idr1q1Ki644IKYOHFi1NXVddu/b9++WLNmTVxwwQV9lquts0efPjHo0xxMv4YiUog7VgEp7d69O23YsCFt2LAhRUS666670oYNG9K2bdtSR0dH+tKXvpTGjh2bfvvb36adO3fmfz788MN8GUuWLElf/epX84937dqVamtr04IFC1JjY2NauXJlqqqq6nUJgW984xtp48aN6YEHHrCEwADrWhrkgQceSBs3bkw33XRTGj58eGpqakopfbQ0SHV1dVq5cmVqbGxMCxYs6LE0iLYufvp06dCnS4d+Ddkh2EKBPPfccykievxce+21aevWrb3ui4j03HPP5cu49tpr06xZs7qV+/vf/z59/vOfT+Xl5amuri4tXbo0v3xAl9WrV6dPf/rTaciQIWnChAnp3nvvPQ5nXNruueee9IlPfCINGTIknXvuuWnNmjX5fZ2dnen2229PdXV1qby8PF144YWpsbGx2/O1dfHTp0uLPl0a9GvIjlxKf70SHQAAADLINbYAAABkmmALAABApgm2AAAAZJpgCwAAQKYJtgAAAGSaYAsAAECmCbYAAABkmmALAH1YunRpfOpTnzrur7t69erI5XKRy+XisssuO+6v32XChAn5euzatatg9QCAwxFsAShJXYGtr5+FCxfGt7/97Xj22WcLVsdNmzbFQw89lH88e/bsuOmmm3oc98QTT0Qul8sf0995TZgwISIimpubY/HixTFp0qQoLy+PcePGxaWXXtrtfF955ZVYsWLFQJ4iABwTJxW6AgBQCDt37sz/+7HHHovvfe97sWnTpvy2ioqKGDFiRIwYMaIQ1YuIiFNPPTVGjhx5VM9ZuXJl7Nu3LyIitm/fHjNmzIhnnnkmpk2bFhERgwcPjqampvjc5z4XI0eOjB/+8Idx9tlnR0dHR/zyl7+MRYsWxeuvvx4REaecckrU1NQc03MCgIFgxBaAklRXV5f/qa6ujlwu12PboVORFy5cGJdddln84Ac/iNra2hg5cmTccccdsX///rj55pujpqYmxo4dGz/5yU+6vdY777wTV111VZx88skxatSomD9/fjQ1NQ3IedXU1OTP4ZRTTomIiFGjRnXbdsMNN0Qul4uXX345vvzlL8fpp58e06ZNi29+85uxbt26AakXAAwkwRYAjsKvfvWr2LFjRzQ0NMRdd90VS5cujUsuuSROPvnkeOmll+L666+P66+/PrZv3x4REe3t7TFnzpwYMWJENDQ0xPPPPx8jRoyIL37xi/mR1ePpvffei6eeeioWLVoUw4cP77H/aEeIAaAYCLYAcBRqamri7rvvjilTpsR1110XU6ZMifb29rjtttti8uTJceutt8aQIUPi17/+dUREPProozFo0KD48Y9/HGeddVacccYZ8eCDD8Zbb70Vq1evPu71f+ONNyKlFFOnTj3urw0AA8U1tgBwFKZNmxaDBn38vXBtbW2ceeaZ+ceDBw+OUaNGxbvvvhsREevXr4833ngjKisru5Wzd+/e2LJly/Gp9EFSShER+ZtNAcCJQLAFgKNQVlbW7XEul+t1W2dnZ0REdHZ2xvTp0+OnP/1pj7K6roE9UlVVVdHa2tpj+65du6KqquqIypg8eXLkcrl47bXXCrqUEAAcS6YiA8AAOvfcc2Pz5s1x6qmnxic/+cluP9XV1UdV1tSpU+PVV1/tsf2VV16JKVOmHFEZNTU1MW/evLjnnnvigw8+6LHferUAZJFgCwAD6Ctf+UqMHj065s+fH2vXro2tW7fGmjVr4sYbb4y33377qMq64YYbYsuWLbFo0aL43e9+F3/605/innvuiQceeCBuvvnmIy5n+fLlceDAgZgxY0asWLEiNm/eHK+99lrcfffdMXPmzKM9RQAoOMEWAAbQsGHDoqGhIcaPHx+XX355nHHGGXHdddfFnj17jnj6cJcJEybE2rVrY8uWLTF37tw4//zz46GHHoqHHnoorrjiiiMuZ+LEifGb3/wm5syZE9/61rfizDPPjIsuuiieffbZuPfee4/2FAGg4HKp6y4SAEBRWL16dcyZMyfef//9gi+/U0x1AYC+GLEFgCI1duzYWLBgQcFef9q0aXHxxRcX7PUB4EgZsQWAIrNnz5545513IiJixIgRUVdXV5B6bNu2LTo6OiIiYtKkSd2WOQKAYiLYAgAAkGm+egUAACDTBFsAAAAyTbAFAAAg0wRbAAAAMk2wBQAAINMEWwAAADJNsAUAACDTBFsAAAAyTbAFAAAg0wRbAAAAMk2wBQAAINMEWwAAADJNsAUAACDTBFsAAAAyTbAFAAAg0wRbAAAAMk2wBQAAINMEWwAAADJNsAUAACDTBFsAAAAyTbAFAAAg0wRbAAAAMk2wBQAAINMEWwAAADJNsAUAACDTBFsAAAAyTbAFAAAg0wRbAAAAMk2wBQAAINMEWwAAADJNsAUAACDTBFsAAAAyTbAFAAAg0wRbAAAAMk2wBQAAINMEWwAAADJNsAUAACDTBFsAAAAyTbAFAAAg0wRbAAAAMk2wBQAAINMEWwAAADJNsAUAACDTBFsAAAAyTbAFAAAg0wRbAAAAMk2wBQAAINMEWwAAADJNsAUAACDTBFsAAAAyTbAFAAAg0wRbAAAAMk2wBQAAINMEWwAAADJNsAUAACDTBFsAAAAyTbAFAAAg0wRbAAAAMk2wBQAAINMEWwAAADJNsAUAACDTBFsAAAAyTbAFAAAg0wRbAAAAMk2wBQAAINMEWwAAADJNsAUAACDTBFsAAAAyTbAFAAAg0wRbAAAAMk2wBQAAINMEWwAAADJNsAUAACDTBFsAAAAyTbAFAAAg0wRbAAAAMk2wBQAAINMEWwAAADJNsAUAACDTBFsAAAAyTbAFAAAg0wRbAAAAMk2wBQAAINMEWwAAADJNsAUAACDTBFsAAAAyTbAFAAAg0wRbAAAAMu3/A4Sl4jH6Fvp7AAAAAElFTkSuQmCC", + "text/html": [ + "\n", + "
\n", + "
\n", + " Figure\n", + "
\n", + " \n", + "
\n", + " " + ], + "text/plain": [ + "Canvas(toolbar=Toolbar(toolitems=[('Home', 'Reset original view', 'home', 'home'), ('Back', 'Back to previous …" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'Bs_B'" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/kefeimo/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/act/utils/datetime_utils.py:136: FutureWarning: Unlike other reduction functions (e.g. `skew`, `kurtosis`), the default behavior of `mode` typically preserves the axis it acts along. In SciPy 1.11.0, this behavior will change: the default value of `keepdims` will become False, the `axis` over which the statistic is taken will be eliminated, and the value None will no longer be accepted. Set `keepdims` to True or False to avoid this warning.\n", + " mode = stats.mode(np.diff(time))\n" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "1b8f0eadc61146b1b9944c7cadc77104", + "version_major": 2, + "version_minor": 0 + }, + "image/png": "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PtzwvLfccovau3dvG+8yUFZWlgpA/fbbbwNuf/jhh1VFUdR9+/YFrF9GRoZ66dIl/3IbN25UAajvv/++6etoj//DH/4QcPu7776rAlBzc3NVVVXV4uJitWXLluqtt94asFx5ebmamZmpXnPNNdLHFhUVqY0bN1YnTJgQ8Nivv/5aBWD43K6//no1NjbWdL316y767/777w/4POzsA3Zo+2Lwfy6XS123bl21npuIiOoGU5GJiMiSenmULbhqcE2YNGlSwN+ffPIJWrRogVtvvRWXLl3y/9e7d2+0a9fOn1bcu3dvxMfH46GHHsI///lPHDp0qMbXTeaaa67B9u3bMW3aNHz++ec4e/asrcetWLECPXr0wDXXXBNw+/333+8fCdcbN24cYmNj/X/36tULAHD48OFqvgOf9evX4/Tp07jvvvsCPuuKigrcfPPNyMnJQXFxsfCxGzZswIULF/CjH/0o4PbBgwejY8eOhuW/+uor/4ioHY8//jhycnKQk5ODlStX4sUXX8SHH36Ie+65x79MTe4DDofD/3rffvstFi5ciK5du2Ls2LHYsGFDlZ+XiIjqBlORiYgasOTkZCQmJiI3N9d0uby8PDgcDrRq1Qrl5eW2HmNHYmIimjdvHnDb8ePHcebMGcTHxwsfc+rUKQBAly5d8OWXX2LOnDl45JFHUFxcjM6dO+MXv/gFHn/88Wqvm5kZM2agSZMmeO+99/DGG28gNjYWw4YNw+zZs9G/f3/p43744Qf/fFe9lJQU//16rVq1CvhbSyn2er3VfAc+x48fBwDccccd0mVOnz6NJk2aGG7X1rVdu3aG+0S3hapDhw4Bn+WIESOgKApmzJiBzz//HKNHj67RfSAmJsaw7UaPHo3U1FQ88cQTDG6JiCIcA1siogYsNjYW119/PZYuXYojR44I59keOXIEmzdvxs033+x/zA033GD6GLtEI8DJyclo1aqVtKpys2bN/P++7rrrcN1116G8vBybNm3yty1q27Yt7r77bjRu3BgAUFpaGvAcWnBcVY0aNcITTzyBJ554AmfOnMGXX36Jp59+GqNHj0ZBQYF0bmerVq3gdrsNtx89ehSA773XJe31XnnllYCKwHpt27YV3q4F3ceOHTPcd+zYMWEAX13aiPX27dsxevRoANb7QHUkJiaiS5cu2L59e7XXnYiIahdTkYmIGris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sQ0mw3xBxLqnW7/bYYa98dPX+vGSC/0nm/sudM9N81keSIpxH7bvDcF/mO2FeizOj71dgInDzT3sem6HEF6KS/g27f0CoSqYucfCxuER64yL+H8o4dWenUaZn2HWs3llKYzIJ7wqdqRjiSrJ6v312nMcOidu98q5hHeYBbGfoQ7mnA0vtNLltt04z79zm57L2zUIOnnM93MiHH1axP0Wi9vxuH0eRTy7/W6I2H48n0WFmGG8nI6O+P9cGHpx7TFsqST256eHAd9z3BdgAuJp0csddzmsdNQQiDo2zD0WNi0XM3yaJSvN5bnsClxr/hyaBo9G3qPgej/b43qLiIjEosDWx7XXXsu1114bNm3ChAlMmDDBdfkLLriACy64wHN7xx13HEuWLIlr37/4xS/4xS9+EXtBEZEjVSqmFoiIiIhIDApsRUSkZfKrASAiIiLioF6RRUREREREJKEpsBUREREREZGEpsBWREREREREEpoCWxEREREREUloCmxFREREREQkoSmwFRERERERkYSmwFZEREREREQSmgJbERERERERSWgKbEVERERERCShKbAVERERERGRhKbAVkRERERERBKaAlsRERERERFJaApsRUREREREJKEpsBUREREREZGEpsBWREREREREEpoCWxEREREREUloCmxFREREREQkoSmwFRERERERkYTWprkTII1ryyH44y4Ylg6fHaydXnoIfr0TCJi/66P0EEzb4b1+rPktRX3T2djHl+jbP5x9NvRyDbWec337+fnsYMOeRztt9d1uS7pvPjsIH+yHJQcb/jzFoyHPhdu2muIZaqh9xNrOZwfhnf3m99Zq9++Hhnoum+Me3VMT/Xc86Tjc59G5348OhH8Px5OGxjxXDZEPqM8+m+M5EpHWIRAMBoPNnQhpeOXl5WRnZ8MUIL25UyPSshy1DZ6aA1deBKs6N3dqRKQp9E0xv7+oavr99t4JM57SZ47IEe8AMBPKysrIyspq7tS0OiqxFZFWJ7MKRpaa3yLSOjR1QOvcb/s9+swREWlsamMrIiIiIiIiCU2BrYiIiIiIiCQ0BbYiIiIiIiKS0BTYioiIiIiISEJTYCsiIiIiIiIJTYGtiIiIiIiIJDQFtiIiIiIiIpLQFNiKiIiIiIhIQlNgRzVgHAAAdLVJREFUKyIiIiIiIglNga2IiIiIiIgkNAW2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YQoNnxE4iBnvcALh4+GKJIgRm+HTR2HkDbCoPsGSNK2XMdW6z23uhIjUjz5zRFqJA8BMKCsr8649KY1Gge0RqkED21JMtbHumAf2XUwJws3Ur52nSDNTJjPBfIKpJt411oIiLZM+c0RaCQW2zUpVkSU+H2CC22RMdbJvoqBWRJrG6OZOgIiIiLR0CmwltnzM8CQiIiIiIiItkHpFFhERERERkYSmwFZEREREREQSmgJbERERERERSWgKbEVERERERCShKbAVERERERGRhKbAVkRERERERBKaAlsRERERERFJaApsRUREREREJKEpsBUREREREZGEpsBWREREREREEpoCWxEREREREUloCmxFREREREQkoSmwFRERERERkYSmwFZEREREREQSmgJbERERERERSWgKbEVERERERCShKbAVERERERGRhKbAVkRandL2MO1k81tEpLHpM0dEpPEFgsFgsLkTIQ2vvLyc7OxsmAKk+y97VAo8lRffdq/cAquqmnZ7jaEuaYxXvMfSnPuOV7xpbK79Nsa+WyOd76bVnOdb1zrxNdfnsojUwQFgJpSVlZGVldXcqWl12jR3AqT5ZSbByBjBr3PZpt5eY6hLGuuyzZa+77psL540Ntd+G2PfrZHOd9NqzvOta534mutzWUQkUejjT0RERERERBKaAlsRERERERFJaApsRUREREREJKEpsBUREREREZGEpsBWREREREREEpoCWxEREREREUloCmxFREREREQkoSmwFRERERERkYSmwFZEREREREQSmgJbERERERERSWgKbEVERERERCShKbAVERERERGRhKbAVkRERERERBKaAlsRERERERFJaApsRUREREREJKEpsBWRuDz6FhT8HyRdDQ++AtOehZW/q8MGdgHTgFLvRfashcBVsHvf4aW1RXob+BXmHKwAngP+2ZwJqpvP7gPmN2MCFgEzmnH/9TUNc71FRESkUbVp7gRIM6gBPsJkFHfA0hQ4uz/87AI44ajmTZq0TOX74Xuz4P6r4eIxkJ0JNUGYMwiKmztxiWAb8C5wGdADyABWNmuKGt7bwG7gQuAB4AKglzVvGubYBzZxmpprv04/xFzvhvQEkAecXbfVrv0TFHWGaRebF0jrHjT/i4iIHAlUYhshEAj4/lx77bX13nZRUREPPvhgzOXGjx8ftd/LL7+83vsN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", + "text/html": [ + "\n", + "
\n", + "
\n", + " Figure\n", + "
\n", + " \n", + "
\n", + " " + ], + "text/plain": [ + "Canvas(toolbar=Toolbar(toolitems=[('Home', 'Reset original view', 'home', 'home'), ('Back', 'Back to previous …" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "3d3082e5808440dc89d3713bcdf41d09", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "AppLayout(children=(Dropdown(description='Field:', index=1, layout=Layout(grid_area='header', margin='0px 30% …" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'Bs_B'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3c0aba93", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/AOP/AOP_tutorial.ipynb b/VAPs/quicklook/AOP/AOP_tutorial.ipynb new file mode 100644 index 00000000..1a07cebb --- /dev/null +++ b/VAPs/quicklook/AOP/AOP_tutorial.ipynb @@ -0,0 +1,838 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# AOPCLAP1FLYNN1M.C1 Notebook\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/aop) for more information about this vap." + ] + }, + { + "cell_type": "markdown", + "id": "97097763", + "metadata": {}, + "source": [ + "In this notebook, we demonstrate the workflow to explore ARM vap data (using aopclap1flynn1m as an example.) Value-added products (VAPs) are higher-order data products that have been analyzed and processed to ease scientists’ use of ARM data in atmospheric research and global climate models. \n", + "Here is the main content we will cover." + ] + }, + { + "cell_type": "markdown", + "id": "eddec40f", + "metadata": {}, + "source": [ + "# Table of Content\n", + "## Access the data\n", + "* How to retrieve the data\n", + "* Data path and file name conventions\n", + "* Load data\n", + "## Explore the data\n", + "* NetCDF Data structure\n", + "* Xarray essentials\n", + "* Xarray Variable\n", + "## Plot the data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "import random\n", + "\n", + "import glob\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "2372d5be", + "metadata": {}, + "source": [ + "## Access the data" + ] + }, + { + "cell_type": "markdown", + "id": "5b0d9684", + "metadata": {}, + "source": [ + "### How to retrieve the data\n", + "We assume the path \"/data/archive\" is available where you are running this notebook. You can use `os.path.exists(\"/data/archive\")` to verify if the path exists at your machine.\n", + "\n", + "\n", + "### Data path and file name conventions\n", + "There several common terminologies regarding ARM data, for example, data-stream-name, data-level, site, facility, instrument, etc. (For more details, please see [ARM Data File Standards Version 1.2](https://www.google.com/search?q=arm+datastream+facility+cite+definition&rlz=1C1GCEJ_enUS1029US1029&ei=hb41ZICrBPukqtsPvMODIA&ved=0ahUKEwjAgeiS0aL-AhV7kmoFHbzhAAQQ4dUDCBA&uact=5&oq=arm+datastream+facility+cite+definition&gs_lcp=Cgxnd3Mtd2l6LXNlcnAQAzIFCAAQogQyBQgAEKIEMgUIABCiBDIFCAAQogQyBQgAEKIEOgoIABBHENYEELADOgoIIRCgARDDBBAKSgQIQRgAUJ4DWIoOYO0PaAFwAXgAgAGMAYgBuQmSAQMyLjmYAQCgAQHIAQjAAQE&sclient=gws-wiz-serp).) \n", + "\n", + "For example, this notebook is called `aopclap1flynn1m.c1`, where `aopclap1flynn1m` is the \"datastream name\", and the `{process.ds_class_level}}` is the \"data level\".\n", + "\n", + "This datastream also contains site `sgp` and facility `C1`. (Note: individual datastream might have multiple site-facility pairs.)\n", + "In such a case, the data of this data-stream is stored at `/data/archive/sgp/sgpaopclap1flynn1mC1.c1`, which is in the format of `//.`. We can use the following method to assign the data-stream directory `datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )`\n", + "\n", + "The data files under datastream_dir also follows naming conventions. But once reach the datastream_dir level, the most import file naming convention to differentiate the files is \"yyyyMMdd.hhmmss\", which comes handy to filter out files based on datetime. For example, we can use `glob.glob(f'{datastream_dir}/*.200709*.*')` to filter files in 2007 September.\n", + "\n", + "(Note: refer to the https://adc.arm.gov/discovery/#/ and https://adc.arm.gov/solr8/metadata/select API to explore ARM datastream and assoicated available sites and facilities\n", + "\n", + "Please see the following examples in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d7e9eb85", + "metadata": {}, + "outputs": [], + "source": [ + "# Verify if DATA_DIR path exists\n", + "DATA_DIR = \"/data/archive\"\n", + "os.path.exists(DATA_DIR)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "586993fd", + "metadata": {}, + "outputs": [], + "source": [ + "# Speicify datastream_dir following the path conventions and check its existence\n", + "DATASTREAM_NAME = \"aopclap1flynn1m\"\n", + "DATA_LEVEL = \"c1\"\n", + "site = \"sgp\"\n", + "facility = \"C1\"\n", + "datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )\n", + "print(datastream_dir)\n", + "print(os.path.exists(datastream_dir))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0742f7c1", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: list 5 (random) files under datastream_dir\n", + "files = os.listdir(datastream_dir)\n", + "files[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39b98a36", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: get most recent file\n", + "list_of_files = glob.glob(f\"{datastream_dir}/*\") # * means all if need specific format then *.csv\n", + "latest_file = max(list_of_files, key=os.path.getctime)\n", + "latest_file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "902d514e", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus sort datastream files based on datetime\n", + "files = os.listdir(datastream_dir)\n", + "file_sorted = files.copy()\n", + "file_sorted.sort() \n", + "print(file_sorted[:5])\n", + "\n", + "# to reverse\n", + "file_sorted_reverse = files.copy() \n", + "file_sorted_reverse.sort(reverse=True)\n", + "print(file_sorted_reverse[:5])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec5923b2", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: pattern matching\n", + "# filter the 200709** files under datastream_dir\n", + "files_filter = glob.glob(f'{datastream_dir}/*.200709*.*')\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c4a4aa18", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: use armnotebook_utils.file_filter (TODO) to filter files based on datastream info \n" + ] + }, + { + "cell_type": "markdown", + "id": "cfeb9efc", + "metadata": {}, + "source": [ + "### Load data\n", + "The arm data is stored in [NetCDF](https://en.wikipedia.org/wiki/NetCDF#:~:text=%22NetCDF%20(network%20Common%20Data%20Form,format%20for%20representing%20scientific%20data.) format. We can use xarray's `open_dataset` method to load single file, or `open_mfdataset` to open multiple files. (Note: the latter will still return a single xarray dataset object by combining multiple files.)\n", + "\n", + "Note: open_dataset keeps the file handle open and lazy loads its contents. All parameters are passed directly to open_dataset. It is a preferable method over load_dataset for memory-effiency. (more details, please see [xarray.open_dataset](https://docs.xarray.dev/en/stable/generated/xarray.open_dataset.html))\n", + "\n", + "See the following example in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a440a329", + "metadata": {}, + "outputs": [], + "source": [ + "# open single file\n", + "full_path = latest_file\n", + "print(full_path)\n", + "ds_single = xr.open_dataset(full_path)\n", + "ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0143a3d", + "metadata": {}, + "outputs": [], + "source": [ + "print(ds_single)\n", + "print(type(ds_single))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c0f8939", + "metadata": {}, + "outputs": [], + "source": [ + "# open multiple files\n", + "n_files = 3\n", + "full_paths = [os.path.join(datastream_dir, f_path) for f_path in file_sorted_reverse[:n_files]]\n", + "print(full_paths)\n", + "try: # Note: sometimes multiple files cannot be merged, so we used try except here.\n", + " ds_mutiple = xr.open_mfdataset(full_paths)\n", + " # ds_mutiple\n", + " print(type(ds_mutiple))\n", + " print(ds_mutiple)\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "3fa59943", + "metadata": {}, + "source": [ + "## Explore the data" + ] + }, + { + "cell_type": "markdown", + "id": "571f69b9", + "metadata": {}, + "source": [ + "### NetCDF Data structure \n", + "\n", + "\n", + "(If you are confident with NetCDF basics and xarray essentials, feel free to skip this session.)\n", + "Before we dive into data exploration, there are some eseential concpets we should be familiar with (shown below): \n", + "* Dataset\n", + "* Data array\n", + "* Variable\n", + "* Dimenssion\n", + "* Coordinate\n", + "* Data Type\n", + "* Meta Data (Attributes)\n", + "\n", + "We will not go into details about NetCDF basics and here are some references you might find helpful\n", + "* [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#)\n", + "* [Network Common Data Form (NetCDF)](https://www.unidata.ucar.edu/software/netcdf/) and [A Brief History of (netCDF) Time](https://www.unidata.ucar.edu/software/netcdf/time/recs.html)\n", + "\n", + "\n", + "\n", + "### Xarray essentials\n", + "Earlier we introduced the xarray pacakge and used `open_dataset` and `open_mfdataset` to retrieve NetCDF data file as an xarray Dataset object. Recall that you can review the data in a notebook by using `print(ds)` or `ds`. \n", + "\n", + "Using xarray to retrieve the aforementioned NetCDF basics is straightforwared. In general,\n", + "* Dataset: `ds`\n", + "* Data array: `ds.variables`\n", + "* Variable: `ds.ds.variables`\n", + "* Dimenssion: `ds.dims`\n", + "* Coordinate: `ds.coords`\n", + "* Data Type: `type`\n", + "* Meta Data (Attributes)\n", + "\n", + "Also, here are some references if you are new to xarray\n", + "* [Xarray in 45 minutes](https://tutorial.xarray.dev/overview/xarray-in-45-min.html)\n", + "* [Handling NetCDF Files using XArray for Absolute Beginners](https://towardsdatascience.com/handling-netcdf-files-using-xarray-for-absolute-beginners-111a8ab4463f)\n", + "\n", + "Try the following commands in action\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b3eac323", + "metadata": {}, + "outputs": [], + "source": [ + "ds = ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77ecf85d", + "metadata": {}, + "outputs": [], + "source": [ + "# Dataset \n", + "ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25e7de09", + "metadata": {}, + "outputs": [], + "source": [ + "# Data array, Variable\n", + "# Note: the info can be overwhelming. Do not attempt to grasp everything at the first glance.\n", + "ds.variables" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c41b67e", + "metadata": {}, + "outputs": [], + "source": [ + "# dimenssions\n", + "ds.dims" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "156f1dfc", + "metadata": {}, + "outputs": [], + "source": [ + "# coordinates\n", + "ds.coords" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "277d6064", + "metadata": {}, + "outputs": [], + "source": [ + "# Meta Data (Attributes)\n", + "ds.attrs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d334681f", + "metadata": {}, + "outputs": [], + "source": [ + "# type\n", + "print(type(ds))\n", + "print(type(ds.variables))\n", + "print(type(ds.dims))\n", + "print(type(ds.coords))\n", + "print(type(ds.attrs))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "643399d6", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: retrieve variable names only\n", + "list(ds.variables)" + ] + }, + { + "cell_type": "markdown", + "id": "1cf36878", + "metadata": {}, + "source": [ + "#### Discussion: variable vs. coordinates vs. dimenssions. \n", + "From [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#):\n", + "\n", + "* Dimession: A dimension may be used to represent a real physical dimension, for example, time, latitude, longitude, or height. A dimension might also be used to index other quantities, for example station or model-run-number.\n", + "A netCDF dimension has both a name and a length. A dimension length is an arbitrary positive integer, except that one dimension in a netCDF dataset can have the length UNLIMITED.\n", + "\n", + "* Variables: Variables are used to store the bulk of the data in a netCDF dataset. A variable represents an array of values of the same type. A scalar value is treated as a 0-dimensional array. A variable has a name, a data type, and a shape described by its list of dimensions specified when the variable is created. A variable may also have associated attributes, which may be added, deleted or changed after the variable is created.\n", + "\n", + "* Coordinate (Variables): It is legal for a variable to have the same name as a dimension. Such variables have no special meaning to the netCDF library. However there is a convention that such variables should be treated in a special way by software using this library.\n", + "A variable with the same name as a dimension is called a coordinate variable. It typically defines a physical coordinate corresponding to that dimension.\n", + "\n", + "\n", + "Tips: By definition, coordinates and dimenssions are also variable. For example ds.time is a coordinate but it is also a special variable. But in practice, when we talk about variable, it implies regular/non-coordinate variable. We will use this convention for the remaining notebook. " + ] + }, + { + "cell_type": "markdown", + "id": "d47120f9", + "metadata": {}, + "source": [ + "### Xarray Variable\n", + "\n", + "\n", + "We can use `ds.variables` to access varialbes of a dataset. But many times viewing print-out the whole datasets can be overwhemling. Instead, we would work on the individual variable (also called Xarray data array.) using the `ds[var_name]` syntax, where `var_name` is the name of the variable. See the following examples in action.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5a57e136", + "metadata": {}, + "outputs": [], + "source": [ + "print(type(ds[\"time\"]))\n", + "ds[\"time\"]" + ] + }, + { + "cell_type": "markdown", + "id": "51ec643e", + "metadata": {}, + "source": [ + "#### Variable properties\n", + "For individual varible, the following properties are mostly used with the assocaited xarray method to retrieve them. (assuming var = ds[var_name])\n", + "* name: `var.name`\n", + "* data content: `var.data`\n", + "* attributes: `var.attrs`\n", + "* dimenstions: `var.dims`\n", + "* data type: `var.data.dtype`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a632a525", + "metadata": {}, + "outputs": [], + "source": [ + "var_name = \"time\"\n", + "var = ds[var_name]\n", + "\n", + "print(\"var.name: \\n\", var.name, \"\\n\")\n", + "print(\"var.data: \\n\", var.data, \"\\n\")\n", + "print(\"var.attrs: \\n\", var.attrs, \"\\n\")\n", + "print(\"var.dims: \\n\", var.dims, \"\\n\")\n", + "print(\"var.data.dtype: \\n\", var.data.dtype, \"\\n\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b256b07f", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: indivdual variable info table (collect and display variable info as a tabular format)\n", + "df_info = pd.DataFrame()\n", + "df_info[\"var_name\"] = list(ds.variables)\n", + "df_info[\"dims\"] = df_info.var_name.apply(lambda x: ds[x].dims)\n", + "df_info[\"is_dim\"] = df_info.var_name.apply(lambda x: x in ds.dims)\n", + "df_info[\"n_dim\"] = df_info.var_name.apply(lambda x: len(ds[x].dims))\n", + "df_info[\"attrs\"] = df_info.var_name.apply(lambda x: ds[x].attrs)\n", + "df_info[\"dtype\"] = df_info.var_name.apply(lambda x: ds[x].data.dtype)\n", + "df_info" + ] + }, + { + "cell_type": "markdown", + "id": "e6a36b96", + "metadata": {}, + "source": [ + "### Data cleaning/Preprocessing (skipped)\n", + "Data cleaning and preprocessing is an important stage in the the data analysis pipeline. However it will be out of the scope of this notebook. For more information about data cleaning and preprocessing basics, here are some references. \n", + "* [Xarray Fundamentals](https://earth-env-data-science.github.io/lectures/xarray/xarray.html)\n", + "* [Xarray Tutorial — Pangeo Gallery documentation](http://gallery.pangeo.io/repos/pangeo-data/pangeo-tutorial-gallery/xarray.html)\n", + "* [Pythonic Data Cleaning With pandas and NumPy](https://realpython.com/python-data-cleaning-numpy-pandas/)\n", + "* [Pandas - Cleaning Data](https://www.w3schools.com/python/pandas/pandas_cleaning.asp)" + ] + }, + { + "cell_type": "markdown", + "id": "ae6e1a69", + "metadata": {}, + "source": [ + "### Plotting\n", + "Data visualization (or plotting) is another important data analysis topic and deserves its own discussion. Here in this notebook we will only demonstrate basic tool for simple data visualization tasks.\n", + "\n", + "Here are some reference you might find useful:\n", + "* xarray plotting: https://docs.xarray.dev/en/stable/user-guide/plotting.html\n", + "* Atmospheric Community Toolkit (ACT): https://arm-doe.github.io/ACT/index.html\n", + "\n", + "Note: this notebook is auto-generated using a template. It uses a general idea to select variable(s) to plot and is not customized to each indivdual notebook. Feel free to change the variables in intrrests, especially certain figure is failed to plot." + ] + }, + { + "cell_type": "markdown", + "id": "7e417fb2", + "metadata": {}, + "source": [ + "#### 1-dimenssional basic time series plot\n", + "\n", + "For the following plot we would like to find variables such that\n", + "* it has one and only one dimession\n", + "* \"time\" is its coordinate variable\n", + "* it is not a dimenssion itself,\n", + "* it is not a special variable with substrings within [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "\n", + "Please see the following example in action. (For more details about pandas filtering, please see the following references.)\n", + "* [pandas: multiple conditions while indexing data frame](https://stackoverflow.com/questions/22591174/pandas-multiple-conditions-while-indexing-data-frame-unexpected-behavior)\n", + "* [pandas.DataFrame.apply](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.apply.html)\n", + "* [How to test if a string contains one of the substrings in a list...](https://stackoverflow.com/questions/26577516/how-to-test-if-a-string-contains-one-of-the-substrings-in-a-list-in-pandas" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30edac2d", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==1, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter = df_info[(df_info.n_dim == 1) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4777a659", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_1d = df_filter.var_name.values[0]\n", + " var_1d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb5ae985", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " ds[var_1d].plot()\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "9e41066e", + "metadata": {}, + "source": [ + "#### 2-dimenssional basic plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09bd4adc", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==2, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_2 = df_info[(df_info.n_dim == 2) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2cdea1a9", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_2d = df_filter_2.var_name.values[0]\n", + " var_2d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5f48879", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " print(ds[var_2d].dims)\n", + " # ds[var_2d].plot()\n", + "\n", + " # conventionally, use \"time\" as x-axis\n", + " ds[var_2d].plot(x=\"time\")\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "7554b2a6", + "metadata": {}, + "source": [ + "#### qc-plotting (optional)\n", + "\n", + "Note: act qc plotting has more strict requirement, one of them is the associated qc variable need to have \"flag_masks\" attributes. Which is added by using ds = act.io.armfiles.read_netcdf(files_list), then ds.clean.cleanup()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71c2096f", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " ds_act = act.io.armfiles.read_netcdf(full_path)\n", + " print(type(ds_act))\n", + " ds_act.clean.cleanup()\n", + "\n", + " # or \n", + " # ds.clean.cleanup()\n", + "except Exception as e:\n", + " print(\"ERROR\", e)\n", + " ds_act = ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "844f8505", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter valid variables for ACT qc plotting\n", + "condition = (\"qc_\" + df_info.var_name).apply(lambda x: ds_act[x].attrs.get(\"flag_masks\") is not None \n", + " if x in list(ds.variables) else False)\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_3 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings))) &\n", + " condition\n", + " ]\n", + "df_filter_3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd38cc6", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "try:\n", + " qc_variable = df_filter_3.var_name.values[0]\n", + " print(qc_variable)\n", + "except Exception as e:\n", + " print(e)\n", + " \n", + "try:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + " qc_ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "d76e4d27", + "metadata": {}, + "source": [ + "#### bonus: choose variables to plot from a dropdown menu " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb733804", + "metadata": {}, + "outputs": [], + "source": [ + "# Valid variables filtering\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_4 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82d5c20d", + "metadata": {}, + "outputs": [], + "source": [ + "# example 1: using xarray plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2fc9aaa4", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# fig, ax = plt.subplots(figsize=(10, 4))\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "# fig.clear() # Remove old lines from plot and plot new one\n", + "# if len(ds[var].dims)==2:\n", + "# ds[var].plot(x=\"time\", add_colorbar=False)\n", + "# else:\n", + "# ds[var].plot()\n", + "# plt.grid()\n", + "# plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fabdd802", + "metadata": {}, + "outputs": [], + "source": [ + "# example 2: using act plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "962b4186", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "\n", + "\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "\n", + "# i_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + "# i_display.add_subplots((1,), figsize=(10, 4))\n", + "# ax = i_display.plot(var, subplot_index=(0,), set_title=f\"{var} ({ds_act[var].attrs['long_name']})\",)\n", + "\n", + "# ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "# ax.grid()\n", + "# plt.show()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/AOP/aopclap1flynn1m.c1.ipynb b/VAPs/quicklook/AOP/aopclap1flynn1m.c1.ipynb new file mode 100644 index 00000000..9c99a80e --- /dev/null +++ b/VAPs/quicklook/AOP/aopclap1flynn1m.c1.ipynb @@ -0,0 +1,339 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# AOPCLAP1FLYNN1M.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/aop) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'aopclap1flynn1m'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2017-09-29', 'facility': 'C1', 'site': 'sgp', 'start_date': '2016-10-31'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2017-09-27'\n", + "date_end = '2017-09-29'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['Bs_B', 'Bs_G', 'Bs_R']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'Bs_B'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'Bs_B'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/AOP/aoppsap1flynn1h.c1.ipynb b/VAPs/quicklook/AOP/aoppsap1flynn1h.c1.ipynb new file mode 100644 index 00000000..8fa832c9 --- /dev/null +++ b/VAPs/quicklook/AOP/aoppsap1flynn1h.c1.ipynb @@ -0,0 +1,339 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# AOPPSAP1FLYNN1H.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/aop) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'aoppsap1flynn1h'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2019-04-30', 'facility': 'M1', 'site': 'cor', 'start_date': '2018-09-23'}, {'end_date': '2017-11-01', 'facility': 'M1', 'site': 'asi', 'start_date': '2016-04-23'}, {'end_date': '2020-06-01', 'facility': 'M1', 'site': 'anx', 'start_date': '2019-12-01'}, {'end_date': '2023-12-11', 'facility': 'M1', 'site': 'epc', 'start_date': '2023-01-15'}, {'end_date': '2022-09-29', 'facility': 'M1', 'site': 'hou', 'start_date': '2021-09-08'}, {'end_date': '2023-12-15', 'facility': 'C1', 'site': 'ena', 'start_date': '2013-10-09'}, {'end_date': '2015-12-01', 'facility': 'S1', 'site': 'mao', 'start_date': '2014-02-06'}, {'end_date': '2021-10-13', 'facility': 'M1', 'site': 'guc', 'start_date': '2021-09-02'}, {'end_date': '2023-06-14', 'facility': 'S2', 'site': 'guc', 'start_date': '2021-10-27'}, {'end_date': '2020-10-01', 'facility': 'M1', 'site': 'mos', 'start_date': '2019-10-11'}, {'end_date': '2021-06-13', 'facility': 'M1', 'site': 'oli', 'start_date': '2016-08-06'}, {'end_date': '2017-09-28', 'facility': 'C1', 'site': 'sgp', 'start_date': '2015-10-01'}, {'end_date': '2023-12-17', 'facility': 'E13', 'site': 'sgp', 'start_date': '2016-11-15'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2017-09-26'\n", + "date_end = '2017-09-28'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['Bs_G_1um', 'Bs_R_1um', 'Bbs_B_1um']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'Bs_B_1um'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'Bs_G_1um'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/AOP/aoppsap1flynn1m.c1.ipynb b/VAPs/quicklook/AOP/aoppsap1flynn1m.c1.ipynb new file mode 100644 index 00000000..2b230f98 --- /dev/null +++ b/VAPs/quicklook/AOP/aoppsap1flynn1m.c1.ipynb @@ -0,0 +1,339 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# AOPPSAP1FLYNN1M.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/aop) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'aoppsap1flynn1m'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2019-04-30', 'facility': 'M1', 'site': 'cor', 'start_date': '2018-09-23'}, {'end_date': '2017-11-01', 'facility': 'M1', 'site': 'asi', 'start_date': '2016-04-23'}, {'end_date': '2020-06-01', 'facility': 'M1', 'site': 'anx', 'start_date': '2019-12-01'}, {'end_date': '2023-12-13', 'facility': 'M1', 'site': 'epc', 'start_date': '2023-01-15'}, {'end_date': '2018-01-11', 'facility': 'M1', 'site': 'mar', 'start_date': '2017-10-29'}, {'end_date': '2022-09-30', 'facility': 'M1', 'site': 'hou', 'start_date': '2021-09-08'}, {'end_date': '2023-12-17', 'facility': 'C1', 'site': 'ena', 'start_date': '2013-10-09'}, {'end_date': '2015-12-01', 'facility': 'S1', 'site': 'mao', 'start_date': '2014-02-06'}, {'end_date': '2021-10-14', 'facility': 'M1', 'site': 'guc', 'start_date': '2021-09-02'}, {'end_date': '2023-06-15', 'facility': 'S2', 'site': 'guc', 'start_date': '2021-10-27'}, {'end_date': '2020-10-01', 'facility': 'M1', 'site': 'mos', 'start_date': '2019-10-11'}, {'end_date': '2021-06-14', 'facility': 'M1', 'site': 'oli', 'start_date': '2016-08-06'}, {'end_date': '2017-09-29', 'facility': 'C1', 'site': 'sgp', 'start_date': '2015-10-01'}, {'end_date': '2023-12-18', 'facility': 'E13', 'site': 'sgp', 'start_date': '2016-11-15'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2017-09-27'\n", + "date_end = '2017-09-29'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['Bs_B', 'Bs_G', 'Bs_R']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'Bs_B'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'Bs_B'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/AOSCCNAVG/AOSCCNAVG_tutorial.ipynb b/VAPs/quicklook/AOSCCNAVG/AOSCCNAVG_tutorial.ipynb new file mode 100644 index 00000000..8761225d --- /dev/null +++ b/VAPs/quicklook/AOSCCNAVG/AOSCCNAVG_tutorial.ipynb @@ -0,0 +1,838 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# AOSCCNAVG.C1 Notebook\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/aosccnavg) for more information about this vap." + ] + }, + { + "cell_type": "markdown", + "id": "97097763", + "metadata": {}, + "source": [ + "In this notebook, we demonstrate the workflow to explore ARM vap data (using aosccnavg as an example.) Value-added products (VAPs) are higher-order data products that have been analyzed and processed to ease scientists’ use of ARM data in atmospheric research and global climate models. \n", + "Here is the main content we will cover." + ] + }, + { + "cell_type": "markdown", + "id": "eddec40f", + "metadata": {}, + "source": [ + "# Table of Content\n", + "## Access the data\n", + "* How to retrieve the data\n", + "* Data path and file name conventions\n", + "* Load data\n", + "## Explore the data\n", + "* NetCDF Data structure\n", + "* Xarray essentials\n", + "* Xarray Variable\n", + "## Plot the data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "import random\n", + "\n", + "import glob\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "2372d5be", + "metadata": {}, + "source": [ + "## Access the data" + ] + }, + { + "cell_type": "markdown", + "id": "5b0d9684", + "metadata": {}, + "source": [ + "### How to retrieve the data\n", + "We assume the path \"/data/archive\" is available where you are running this notebook. You can use `os.path.exists(\"/data/archive\")` to verify if the path exists at your machine.\n", + "\n", + "\n", + "### Data path and file name conventions\n", + "There several common terminologies regarding ARM data, for example, data-stream-name, data-level, site, facility, instrument, etc. (For more details, please see [ARM Data File Standards Version 1.2](https://www.google.com/search?q=arm+datastream+facility+cite+definition&rlz=1C1GCEJ_enUS1029US1029&ei=hb41ZICrBPukqtsPvMODIA&ved=0ahUKEwjAgeiS0aL-AhV7kmoFHbzhAAQQ4dUDCBA&uact=5&oq=arm+datastream+facility+cite+definition&gs_lcp=Cgxnd3Mtd2l6LXNlcnAQAzIFCAAQogQyBQgAEKIEMgUIABCiBDIFCAAQogQyBQgAEKIEOgoIABBHENYEELADOgoIIRCgARDDBBAKSgQIQRgAUJ4DWIoOYO0PaAFwAXgAgAGMAYgBuQmSAQMyLjmYAQCgAQHIAQjAAQE&sclient=gws-wiz-serp).) \n", + "\n", + "For example, this notebook is called `aosccnavg.c1`, where `aosccnavg` is the \"datastream name\", and the `{process.ds_class_level}}` is the \"data level\".\n", + "\n", + "This datastream also contains site `mao` and facility `M1`. (Note: individual datastream might have multiple site-facility pairs.)\n", + "In such a case, the data of this data-stream is stored at `/data/archive/mao/maoaosccnavgM1.c1`, which is in the format of `//.`. We can use the following method to assign the data-stream directory `datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )`\n", + "\n", + "The data files under datastream_dir also follows naming conventions. But once reach the datastream_dir level, the most import file naming convention to differentiate the files is \"yyyyMMdd.hhmmss\", which comes handy to filter out files based on datetime. For example, we can use `glob.glob(f'{datastream_dir}/*.200709*.*')` to filter files in 2007 September.\n", + "\n", + "(Note: refer to the https://adc.arm.gov/discovery/#/ and https://adc.arm.gov/solr8/metadata/select API to explore ARM datastream and assoicated available sites and facilities\n", + "\n", + "Please see the following examples in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d7e9eb85", + "metadata": {}, + "outputs": [], + "source": [ + "# Verify if DATA_DIR path exists\n", + "DATA_DIR = \"/data/archive\"\n", + "os.path.exists(DATA_DIR)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "586993fd", + "metadata": {}, + "outputs": [], + "source": [ + "# Speicify datastream_dir following the path conventions and check its existence\n", + "DATASTREAM_NAME = \"aosccnavg\"\n", + "DATA_LEVEL = \"c1\"\n", + "site = \"mao\"\n", + "facility = \"M1\"\n", + "datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )\n", + "print(datastream_dir)\n", + "print(os.path.exists(datastream_dir))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0742f7c1", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: list 5 (random) files under datastream_dir\n", + "files = os.listdir(datastream_dir)\n", + "files[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39b98a36", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: get most recent file\n", + "list_of_files = glob.glob(f\"{datastream_dir}/*\") # * means all if need specific format then *.csv\n", + "latest_file = max(list_of_files, key=os.path.getctime)\n", + "latest_file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "902d514e", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus sort datastream files based on datetime\n", + "files = os.listdir(datastream_dir)\n", + "file_sorted = files.copy()\n", + "file_sorted.sort() \n", + "print(file_sorted[:5])\n", + "\n", + "# to reverse\n", + "file_sorted_reverse = files.copy() \n", + "file_sorted_reverse.sort(reverse=True)\n", + "print(file_sorted_reverse[:5])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec5923b2", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: pattern matching\n", + "# filter the 200709** files under datastream_dir\n", + "files_filter = glob.glob(f'{datastream_dir}/*.200709*.*')\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c4a4aa18", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: use armnotebook_utils.file_filter (TODO) to filter files based on datastream info \n" + ] + }, + { + "cell_type": "markdown", + "id": "cfeb9efc", + "metadata": {}, + "source": [ + "### Load data\n", + "The arm data is stored in [NetCDF](https://en.wikipedia.org/wiki/NetCDF#:~:text=%22NetCDF%20(network%20Common%20Data%20Form,format%20for%20representing%20scientific%20data.) format. We can use xarray's `open_dataset` method to load single file, or `open_mfdataset` to open multiple files. (Note: the latter will still return a single xarray dataset object by combining multiple files.)\n", + "\n", + "Note: open_dataset keeps the file handle open and lazy loads its contents. All parameters are passed directly to open_dataset. It is a preferable method over load_dataset for memory-effiency. (more details, please see [xarray.open_dataset](https://docs.xarray.dev/en/stable/generated/xarray.open_dataset.html))\n", + "\n", + "See the following example in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a440a329", + "metadata": {}, + "outputs": [], + "source": [ + "# open single file\n", + "full_path = latest_file\n", + "print(full_path)\n", + "ds_single = xr.open_dataset(full_path)\n", + "ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0143a3d", + "metadata": {}, + "outputs": [], + "source": [ + "print(ds_single)\n", + "print(type(ds_single))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c0f8939", + "metadata": {}, + "outputs": [], + "source": [ + "# open multiple files\n", + "n_files = 3\n", + "full_paths = [os.path.join(datastream_dir, f_path) for f_path in file_sorted_reverse[:n_files]]\n", + "print(full_paths)\n", + "try: # Note: sometimes multiple files cannot be merged, so we used try except here.\n", + " ds_mutiple = xr.open_mfdataset(full_paths)\n", + " # ds_mutiple\n", + " print(type(ds_mutiple))\n", + " print(ds_mutiple)\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "3fa59943", + "metadata": {}, + "source": [ + "## Explore the data" + ] + }, + { + "cell_type": "markdown", + "id": "571f69b9", + "metadata": {}, + "source": [ + "### NetCDF Data structure \n", + "\n", + "\n", + "(If you are confident with NetCDF basics and xarray essentials, feel free to skip this session.)\n", + "Before we dive into data exploration, there are some eseential concpets we should be familiar with (shown below): \n", + "* Dataset\n", + "* Data array\n", + "* Variable\n", + "* Dimenssion\n", + "* Coordinate\n", + "* Data Type\n", + "* Meta Data (Attributes)\n", + "\n", + "We will not go into details about NetCDF basics and here are some references you might find helpful\n", + "* [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#)\n", + "* [Network Common Data Form (NetCDF)](https://www.unidata.ucar.edu/software/netcdf/) and [A Brief History of (netCDF) Time](https://www.unidata.ucar.edu/software/netcdf/time/recs.html)\n", + "\n", + "\n", + "\n", + "### Xarray essentials\n", + "Earlier we introduced the xarray pacakge and used `open_dataset` and `open_mfdataset` to retrieve NetCDF data file as an xarray Dataset object. Recall that you can review the data in a notebook by using `print(ds)` or `ds`. \n", + "\n", + "Using xarray to retrieve the aforementioned NetCDF basics is straightforwared. In general,\n", + "* Dataset: `ds`\n", + "* Data array: `ds.variables`\n", + "* Variable: `ds.ds.variables`\n", + "* Dimenssion: `ds.dims`\n", + "* Coordinate: `ds.coords`\n", + "* Data Type: `type`\n", + "* Meta Data (Attributes)\n", + "\n", + "Also, here are some references if you are new to xarray\n", + "* [Xarray in 45 minutes](https://tutorial.xarray.dev/overview/xarray-in-45-min.html)\n", + "* [Handling NetCDF Files using XArray for Absolute Beginners](https://towardsdatascience.com/handling-netcdf-files-using-xarray-for-absolute-beginners-111a8ab4463f)\n", + "\n", + "Try the following commands in action\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b3eac323", + "metadata": {}, + "outputs": [], + "source": [ + "ds = ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77ecf85d", + "metadata": {}, + "outputs": [], + "source": [ + "# Dataset \n", + "ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25e7de09", + "metadata": {}, + "outputs": [], + "source": [ + "# Data array, Variable\n", + "# Note: the info can be overwhelming. Do not attempt to grasp everything at the first glance.\n", + "ds.variables" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c41b67e", + "metadata": {}, + "outputs": [], + "source": [ + "# dimenssions\n", + "ds.dims" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "156f1dfc", + "metadata": {}, + "outputs": [], + "source": [ + "# coordinates\n", + "ds.coords" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "277d6064", + "metadata": {}, + "outputs": [], + "source": [ + "# Meta Data (Attributes)\n", + "ds.attrs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d334681f", + "metadata": {}, + "outputs": [], + "source": [ + "# type\n", + "print(type(ds))\n", + "print(type(ds.variables))\n", + "print(type(ds.dims))\n", + "print(type(ds.coords))\n", + "print(type(ds.attrs))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "643399d6", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: retrieve variable names only\n", + "list(ds.variables)" + ] + }, + { + "cell_type": "markdown", + "id": "1cf36878", + "metadata": {}, + "source": [ + "#### Discussion: variable vs. coordinates vs. dimenssions. \n", + "From [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#):\n", + "\n", + "* Dimession: A dimension may be used to represent a real physical dimension, for example, time, latitude, longitude, or height. A dimension might also be used to index other quantities, for example station or model-run-number.\n", + "A netCDF dimension has both a name and a length. A dimension length is an arbitrary positive integer, except that one dimension in a netCDF dataset can have the length UNLIMITED.\n", + "\n", + "* Variables: Variables are used to store the bulk of the data in a netCDF dataset. A variable represents an array of values of the same type. A scalar value is treated as a 0-dimensional array. A variable has a name, a data type, and a shape described by its list of dimensions specified when the variable is created. A variable may also have associated attributes, which may be added, deleted or changed after the variable is created.\n", + "\n", + "* Coordinate (Variables): It is legal for a variable to have the same name as a dimension. Such variables have no special meaning to the netCDF library. However there is a convention that such variables should be treated in a special way by software using this library.\n", + "A variable with the same name as a dimension is called a coordinate variable. It typically defines a physical coordinate corresponding to that dimension.\n", + "\n", + "\n", + "Tips: By definition, coordinates and dimenssions are also variable. For example ds.time is a coordinate but it is also a special variable. But in practice, when we talk about variable, it implies regular/non-coordinate variable. We will use this convention for the remaining notebook. " + ] + }, + { + "cell_type": "markdown", + "id": "d47120f9", + "metadata": {}, + "source": [ + "### Xarray Variable\n", + "\n", + "\n", + "We can use `ds.variables` to access varialbes of a dataset. But many times viewing print-out the whole datasets can be overwhemling. Instead, we would work on the individual variable (also called Xarray data array.) using the `ds[var_name]` syntax, where `var_name` is the name of the variable. See the following examples in action.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5a57e136", + "metadata": {}, + "outputs": [], + "source": [ + "print(type(ds[\"time\"]))\n", + "ds[\"time\"]" + ] + }, + { + "cell_type": "markdown", + "id": "51ec643e", + "metadata": {}, + "source": [ + "#### Variable properties\n", + "For individual varible, the following properties are mostly used with the assocaited xarray method to retrieve them. (assuming var = ds[var_name])\n", + "* name: `var.name`\n", + "* data content: `var.data`\n", + "* attributes: `var.attrs`\n", + "* dimenstions: `var.dims`\n", + "* data type: `var.data.dtype`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a632a525", + "metadata": {}, + "outputs": [], + "source": [ + "var_name = \"time\"\n", + "var = ds[var_name]\n", + "\n", + "print(\"var.name: \\n\", var.name, \"\\n\")\n", + "print(\"var.data: \\n\", var.data, \"\\n\")\n", + "print(\"var.attrs: \\n\", var.attrs, \"\\n\")\n", + "print(\"var.dims: \\n\", var.dims, \"\\n\")\n", + "print(\"var.data.dtype: \\n\", var.data.dtype, \"\\n\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b256b07f", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: indivdual variable info table (collect and display variable info as a tabular format)\n", + "df_info = pd.DataFrame()\n", + "df_info[\"var_name\"] = list(ds.variables)\n", + "df_info[\"dims\"] = df_info.var_name.apply(lambda x: ds[x].dims)\n", + "df_info[\"is_dim\"] = df_info.var_name.apply(lambda x: x in ds.dims)\n", + "df_info[\"n_dim\"] = df_info.var_name.apply(lambda x: len(ds[x].dims))\n", + "df_info[\"attrs\"] = df_info.var_name.apply(lambda x: ds[x].attrs)\n", + "df_info[\"dtype\"] = df_info.var_name.apply(lambda x: ds[x].data.dtype)\n", + "df_info" + ] + }, + { + "cell_type": "markdown", + "id": "e6a36b96", + "metadata": {}, + "source": [ + "### Data cleaning/Preprocessing (skipped)\n", + "Data cleaning and preprocessing is an important stage in the the data analysis pipeline. However it will be out of the scope of this notebook. For more information about data cleaning and preprocessing basics, here are some references. \n", + "* [Xarray Fundamentals](https://earth-env-data-science.github.io/lectures/xarray/xarray.html)\n", + "* [Xarray Tutorial — Pangeo Gallery documentation](http://gallery.pangeo.io/repos/pangeo-data/pangeo-tutorial-gallery/xarray.html)\n", + "* [Pythonic Data Cleaning With pandas and NumPy](https://realpython.com/python-data-cleaning-numpy-pandas/)\n", + "* [Pandas - Cleaning Data](https://www.w3schools.com/python/pandas/pandas_cleaning.asp)" + ] + }, + { + "cell_type": "markdown", + "id": "ae6e1a69", + "metadata": {}, + "source": [ + "### Plotting\n", + "Data visualization (or plotting) is another important data analysis topic and deserves its own discussion. Here in this notebook we will only demonstrate basic tool for simple data visualization tasks.\n", + "\n", + "Here are some reference you might find useful:\n", + "* xarray plotting: https://docs.xarray.dev/en/stable/user-guide/plotting.html\n", + "* Atmospheric Community Toolkit (ACT): https://arm-doe.github.io/ACT/index.html\n", + "\n", + "Note: this notebook is auto-generated using a template. It uses a general idea to select variable(s) to plot and is not customized to each indivdual notebook. Feel free to change the variables in intrrests, especially certain figure is failed to plot." + ] + }, + { + "cell_type": "markdown", + "id": "7e417fb2", + "metadata": {}, + "source": [ + "#### 1-dimenssional basic time series plot\n", + "\n", + "For the following plot we would like to find variables such that\n", + "* it has one and only one dimession\n", + "* \"time\" is its coordinate variable\n", + "* it is not a dimenssion itself,\n", + "* it is not a special variable with substrings within [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "\n", + "Please see the following example in action. (For more details about pandas filtering, please see the following references.)\n", + "* [pandas: multiple conditions while indexing data frame](https://stackoverflow.com/questions/22591174/pandas-multiple-conditions-while-indexing-data-frame-unexpected-behavior)\n", + "* [pandas.DataFrame.apply](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.apply.html)\n", + "* [How to test if a string contains one of the substrings in a list...](https://stackoverflow.com/questions/26577516/how-to-test-if-a-string-contains-one-of-the-substrings-in-a-list-in-pandas" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30edac2d", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==1, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter = df_info[(df_info.n_dim == 1) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4777a659", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_1d = df_filter.var_name.values[0]\n", + " var_1d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb5ae985", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " ds[var_1d].plot()\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "9e41066e", + "metadata": {}, + "source": [ + "#### 2-dimenssional basic plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09bd4adc", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==2, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_2 = df_info[(df_info.n_dim == 2) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2cdea1a9", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_2d = df_filter_2.var_name.values[0]\n", + " var_2d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5f48879", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " print(ds[var_2d].dims)\n", + " # ds[var_2d].plot()\n", + "\n", + " # conventionally, use \"time\" as x-axis\n", + " ds[var_2d].plot(x=\"time\")\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "7554b2a6", + "metadata": {}, + "source": [ + "#### qc-plotting (optional)\n", + "\n", + "Note: act qc plotting has more strict requirement, one of them is the associated qc variable need to have \"flag_masks\" attributes. Which is added by using ds = act.io.armfiles.read_netcdf(files_list), then ds.clean.cleanup()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71c2096f", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " ds_act = act.io.armfiles.read_netcdf(full_path)\n", + " print(type(ds_act))\n", + " ds_act.clean.cleanup()\n", + "\n", + " # or \n", + " # ds.clean.cleanup()\n", + "except Exception as e:\n", + " print(\"ERROR\", e)\n", + " ds_act = ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "844f8505", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter valid variables for ACT qc plotting\n", + "condition = (\"qc_\" + df_info.var_name).apply(lambda x: ds_act[x].attrs.get(\"flag_masks\") is not None \n", + " if x in list(ds.variables) else False)\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_3 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings))) &\n", + " condition\n", + " ]\n", + "df_filter_3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd38cc6", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "try:\n", + " qc_variable = df_filter_3.var_name.values[0]\n", + " print(qc_variable)\n", + "except Exception as e:\n", + " print(e)\n", + " \n", + "try:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + " qc_ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "d76e4d27", + "metadata": {}, + "source": [ + "#### bonus: choose variables to plot from a dropdown menu " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb733804", + "metadata": {}, + "outputs": [], + "source": [ + "# Valid variables filtering\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_4 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82d5c20d", + "metadata": {}, + "outputs": [], + "source": [ + "# example 1: using xarray plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2fc9aaa4", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# fig, ax = plt.subplots(figsize=(10, 4))\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "# fig.clear() # Remove old lines from plot and plot new one\n", + "# if len(ds[var].dims)==2:\n", + "# ds[var].plot(x=\"time\", add_colorbar=False)\n", + "# else:\n", + "# ds[var].plot()\n", + "# plt.grid()\n", + "# plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fabdd802", + "metadata": {}, + "outputs": [], + "source": [ + "# example 2: using act plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "962b4186", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "\n", + "\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "\n", + "# i_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + "# i_display.add_subplots((1,), figsize=(10, 4))\n", + "# ax = i_display.plot(var, subplot_index=(0,), set_title=f\"{var} ({ds_act[var].attrs['long_name']})\",)\n", + "\n", + "# ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "# ax.grid()\n", + "# plt.show()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/AOSCCNAVG/aosccnavg.c1.ipynb b/VAPs/quicklook/AOSCCNAVG/aosccnavg.c1.ipynb new file mode 100644 index 00000000..97b1a61f --- /dev/null +++ b/VAPs/quicklook/AOSCCNAVG/aosccnavg.c1.ipynb @@ -0,0 +1,339 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# AOSCCNAVG.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/aosccnavg) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'aosccnavg'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2014-10-19', 'facility': 'M1', 'site': 'mao', 'start_date': '2014-02-19'}, {'end_date': '2013-06-13', 'facility': 'M1', 'site': 'pvc', 'start_date': '2013-03-31'}, {'end_date': '2014-11-05', 'facility': 'C1', 'site': 'sgp', 'start_date': '2007-05-19'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2014-11-03'\n", + "date_end = '2014-11-05'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['N_CCN', 'N_CPC']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'supersaturation_setpoint'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'N_CCN'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/AOSCCNAVG/aosccnavg.c2.ipynb b/VAPs/quicklook/AOSCCNAVG/aosccnavg.c2.ipynb new file mode 100644 index 00000000..4c0fc3b3 --- /dev/null +++ b/VAPs/quicklook/AOSCCNAVG/aosccnavg.c2.ipynb @@ -0,0 +1,339 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# AOSCCNAVG.C2 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/aosccnavg) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'aosccnavg'\n", + "DATA_LEVEL = 'c2'\n", + "LOCATIONS = [{'end_date': '2014-09-29', 'facility': 'M1', 'site': 'mao', 'start_date': '2014-07-01'}, {'end_date': '2010-12-30', 'facility': 'M1', 'site': 'grw', 'start_date': '2009-04-16'}, {'end_date': '2012-03-26', 'facility': 'M1', 'site': 'pgh', 'start_date': '2011-06-09'}, {'end_date': '2013-03-30', 'facility': 'M1', 'site': 'pvc', 'start_date': '2012-07-16'}, {'end_date': '2014-09-29', 'facility': 'C1', 'site': 'sgp', 'start_date': '2011-01-01'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2014-09-27'\n", + "date_end = '2014-09-29'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['N_CCN', 'N_CPC']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'supersaturation_setpoint'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'N_CCN'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/AOSSP2BC/AOSSP2BC_tutorial.ipynb b/VAPs/quicklook/AOSSP2BC/AOSSP2BC_tutorial.ipynb new file mode 100644 index 00000000..b50440af --- /dev/null +++ b/VAPs/quicklook/AOSSP2BC/AOSSP2BC_tutorial.ipynb @@ -0,0 +1,838 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# AOSSP2RBC1M.C1 Notebook\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/aossp2bc) for more information about this vap." + ] + }, + { + "cell_type": "markdown", + "id": "97097763", + "metadata": {}, + "source": [ + "In this notebook, we demonstrate the workflow to explore ARM vap data (using aossp2rbc1m as an example.) Value-added products (VAPs) are higher-order data products that have been analyzed and processed to ease scientists’ use of ARM data in atmospheric research and global climate models. \n", + "Here is the main content we will cover." + ] + }, + { + "cell_type": "markdown", + "id": "eddec40f", + "metadata": {}, + "source": [ + "# Table of Content\n", + "## Access the data\n", + "* How to retrieve the data\n", + "* Data path and file name conventions\n", + "* Load data\n", + "## Explore the data\n", + "* NetCDF Data structure\n", + "* Xarray essentials\n", + "* Xarray Variable\n", + "## Plot the data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "import random\n", + "\n", + "import glob\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "2372d5be", + "metadata": {}, + "source": [ + "## Access the data" + ] + }, + { + "cell_type": "markdown", + "id": "5b0d9684", + "metadata": {}, + "source": [ + "### How to retrieve the data\n", + "We assume the path \"/data/archive\" is available where you are running this notebook. You can use `os.path.exists(\"/data/archive\")` to verify if the path exists at your machine.\n", + "\n", + "\n", + "### Data path and file name conventions\n", + "There several common terminologies regarding ARM data, for example, data-stream-name, data-level, site, facility, instrument, etc. (For more details, please see [ARM Data File Standards Version 1.2](https://www.google.com/search?q=arm+datastream+facility+cite+definition&rlz=1C1GCEJ_enUS1029US1029&ei=hb41ZICrBPukqtsPvMODIA&ved=0ahUKEwjAgeiS0aL-AhV7kmoFHbzhAAQQ4dUDCBA&uact=5&oq=arm+datastream+facility+cite+definition&gs_lcp=Cgxnd3Mtd2l6LXNlcnAQAzIFCAAQogQyBQgAEKIEMgUIABCiBDIFCAAQogQyBQgAEKIEOgoIABBHENYEELADOgoIIRCgARDDBBAKSgQIQRgAUJ4DWIoOYO0PaAFwAXgAgAGMAYgBuQmSAQMyLjmYAQCgAQHIAQjAAQE&sclient=gws-wiz-serp).) \n", + "\n", + "For example, this notebook is called `aossp2rbc1m.c1`, where `aossp2rbc1m` is the \"datastream name\", and the `{process.ds_class_level}}` is the \"data level\".\n", + "\n", + "This datastream also contains site `mao` and facility `S1`. (Note: individual datastream might have multiple site-facility pairs.)\n", + "In such a case, the data of this data-stream is stored at `/data/archive/mao/maoaossp2rbc1mS1.c1`, which is in the format of `//.`. We can use the following method to assign the data-stream directory `datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )`\n", + "\n", + "The data files under datastream_dir also follows naming conventions. But once reach the datastream_dir level, the most import file naming convention to differentiate the files is \"yyyyMMdd.hhmmss\", which comes handy to filter out files based on datetime. For example, we can use `glob.glob(f'{datastream_dir}/*.200709*.*')` to filter files in 2007 September.\n", + "\n", + "(Note: refer to the https://adc.arm.gov/discovery/#/ and https://adc.arm.gov/solr8/metadata/select API to explore ARM datastream and assoicated available sites and facilities\n", + "\n", + "Please see the following examples in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d7e9eb85", + "metadata": {}, + "outputs": [], + "source": [ + "# Verify if DATA_DIR path exists\n", + "DATA_DIR = \"/data/archive\"\n", + "os.path.exists(DATA_DIR)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "586993fd", + "metadata": {}, + "outputs": [], + "source": [ + "# Speicify datastream_dir following the path conventions and check its existence\n", + "DATASTREAM_NAME = \"aossp2rbc1m\"\n", + "DATA_LEVEL = \"c1\"\n", + "site = \"mao\"\n", + "facility = \"S1\"\n", + "datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )\n", + "print(datastream_dir)\n", + "print(os.path.exists(datastream_dir))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0742f7c1", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: list 5 (random) files under datastream_dir\n", + "files = os.listdir(datastream_dir)\n", + "files[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39b98a36", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: get most recent file\n", + "list_of_files = glob.glob(f\"{datastream_dir}/*\") # * means all if need specific format then *.csv\n", + "latest_file = max(list_of_files, key=os.path.getctime)\n", + "latest_file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "902d514e", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus sort datastream files based on datetime\n", + "files = os.listdir(datastream_dir)\n", + "file_sorted = files.copy()\n", + "file_sorted.sort() \n", + "print(file_sorted[:5])\n", + "\n", + "# to reverse\n", + "file_sorted_reverse = files.copy() \n", + "file_sorted_reverse.sort(reverse=True)\n", + "print(file_sorted_reverse[:5])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec5923b2", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: pattern matching\n", + "# filter the 200709** files under datastream_dir\n", + "files_filter = glob.glob(f'{datastream_dir}/*.200709*.*')\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c4a4aa18", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: use armnotebook_utils.file_filter (TODO) to filter files based on datastream info \n" + ] + }, + { + "cell_type": "markdown", + "id": "cfeb9efc", + "metadata": {}, + "source": [ + "### Load data\n", + "The arm data is stored in [NetCDF](https://en.wikipedia.org/wiki/NetCDF#:~:text=%22NetCDF%20(network%20Common%20Data%20Form,format%20for%20representing%20scientific%20data.) format. We can use xarray's `open_dataset` method to load single file, or `open_mfdataset` to open multiple files. (Note: the latter will still return a single xarray dataset object by combining multiple files.)\n", + "\n", + "Note: open_dataset keeps the file handle open and lazy loads its contents. All parameters are passed directly to open_dataset. It is a preferable method over load_dataset for memory-effiency. (more details, please see [xarray.open_dataset](https://docs.xarray.dev/en/stable/generated/xarray.open_dataset.html))\n", + "\n", + "See the following example in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a440a329", + "metadata": {}, + "outputs": [], + "source": [ + "# open single file\n", + "full_path = latest_file\n", + "print(full_path)\n", + "ds_single = xr.open_dataset(full_path)\n", + "ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0143a3d", + "metadata": {}, + "outputs": [], + "source": [ + "print(ds_single)\n", + "print(type(ds_single))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c0f8939", + "metadata": {}, + "outputs": [], + "source": [ + "# open multiple files\n", + "n_files = 3\n", + "full_paths = [os.path.join(datastream_dir, f_path) for f_path in file_sorted_reverse[:n_files]]\n", + "print(full_paths)\n", + "try: # Note: sometimes multiple files cannot be merged, so we used try except here.\n", + " ds_mutiple = xr.open_mfdataset(full_paths)\n", + " # ds_mutiple\n", + " print(type(ds_mutiple))\n", + " print(ds_mutiple)\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "3fa59943", + "metadata": {}, + "source": [ + "## Explore the data" + ] + }, + { + "cell_type": "markdown", + "id": "571f69b9", + "metadata": {}, + "source": [ + "### NetCDF Data structure \n", + "\n", + "\n", + "(If you are confident with NetCDF basics and xarray essentials, feel free to skip this session.)\n", + "Before we dive into data exploration, there are some eseential concpets we should be familiar with (shown below): \n", + "* Dataset\n", + "* Data array\n", + "* Variable\n", + "* Dimenssion\n", + "* Coordinate\n", + "* Data Type\n", + "* Meta Data (Attributes)\n", + "\n", + "We will not go into details about NetCDF basics and here are some references you might find helpful\n", + "* [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#)\n", + "* [Network Common Data Form (NetCDF)](https://www.unidata.ucar.edu/software/netcdf/) and [A Brief History of (netCDF) Time](https://www.unidata.ucar.edu/software/netcdf/time/recs.html)\n", + "\n", + "\n", + "\n", + "### Xarray essentials\n", + "Earlier we introduced the xarray pacakge and used `open_dataset` and `open_mfdataset` to retrieve NetCDF data file as an xarray Dataset object. Recall that you can review the data in a notebook by using `print(ds)` or `ds`. \n", + "\n", + "Using xarray to retrieve the aforementioned NetCDF basics is straightforwared. In general,\n", + "* Dataset: `ds`\n", + "* Data array: `ds.variables`\n", + "* Variable: `ds.ds.variables`\n", + "* Dimenssion: `ds.dims`\n", + "* Coordinate: `ds.coords`\n", + "* Data Type: `type`\n", + "* Meta Data (Attributes)\n", + "\n", + "Also, here are some references if you are new to xarray\n", + "* [Xarray in 45 minutes](https://tutorial.xarray.dev/overview/xarray-in-45-min.html)\n", + "* [Handling NetCDF Files using XArray for Absolute Beginners](https://towardsdatascience.com/handling-netcdf-files-using-xarray-for-absolute-beginners-111a8ab4463f)\n", + "\n", + "Try the following commands in action\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b3eac323", + "metadata": {}, + "outputs": [], + "source": [ + "ds = ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77ecf85d", + "metadata": {}, + "outputs": [], + "source": [ + "# Dataset \n", + "ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25e7de09", + "metadata": {}, + "outputs": [], + "source": [ + "# Data array, Variable\n", + "# Note: the info can be overwhelming. Do not attempt to grasp everything at the first glance.\n", + "ds.variables" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c41b67e", + "metadata": {}, + "outputs": [], + "source": [ + "# dimenssions\n", + "ds.dims" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "156f1dfc", + "metadata": {}, + "outputs": [], + "source": [ + "# coordinates\n", + "ds.coords" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "277d6064", + "metadata": {}, + "outputs": [], + "source": [ + "# Meta Data (Attributes)\n", + "ds.attrs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d334681f", + "metadata": {}, + "outputs": [], + "source": [ + "# type\n", + "print(type(ds))\n", + "print(type(ds.variables))\n", + "print(type(ds.dims))\n", + "print(type(ds.coords))\n", + "print(type(ds.attrs))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "643399d6", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: retrieve variable names only\n", + "list(ds.variables)" + ] + }, + { + "cell_type": "markdown", + "id": "1cf36878", + "metadata": {}, + "source": [ + "#### Discussion: variable vs. coordinates vs. dimenssions. \n", + "From [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#):\n", + "\n", + "* Dimession: A dimension may be used to represent a real physical dimension, for example, time, latitude, longitude, or height. A dimension might also be used to index other quantities, for example station or model-run-number.\n", + "A netCDF dimension has both a name and a length. A dimension length is an arbitrary positive integer, except that one dimension in a netCDF dataset can have the length UNLIMITED.\n", + "\n", + "* Variables: Variables are used to store the bulk of the data in a netCDF dataset. A variable represents an array of values of the same type. A scalar value is treated as a 0-dimensional array. A variable has a name, a data type, and a shape described by its list of dimensions specified when the variable is created. A variable may also have associated attributes, which may be added, deleted or changed after the variable is created.\n", + "\n", + "* Coordinate (Variables): It is legal for a variable to have the same name as a dimension. Such variables have no special meaning to the netCDF library. However there is a convention that such variables should be treated in a special way by software using this library.\n", + "A variable with the same name as a dimension is called a coordinate variable. It typically defines a physical coordinate corresponding to that dimension.\n", + "\n", + "\n", + "Tips: By definition, coordinates and dimenssions are also variable. For example ds.time is a coordinate but it is also a special variable. But in practice, when we talk about variable, it implies regular/non-coordinate variable. We will use this convention for the remaining notebook. " + ] + }, + { + "cell_type": "markdown", + "id": "d47120f9", + "metadata": {}, + "source": [ + "### Xarray Variable\n", + "\n", + "\n", + "We can use `ds.variables` to access varialbes of a dataset. But many times viewing print-out the whole datasets can be overwhemling. Instead, we would work on the individual variable (also called Xarray data array.) using the `ds[var_name]` syntax, where `var_name` is the name of the variable. See the following examples in action.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5a57e136", + "metadata": {}, + "outputs": [], + "source": [ + "print(type(ds[\"time\"]))\n", + "ds[\"time\"]" + ] + }, + { + "cell_type": "markdown", + "id": "51ec643e", + "metadata": {}, + "source": [ + "#### Variable properties\n", + "For individual varible, the following properties are mostly used with the assocaited xarray method to retrieve them. (assuming var = ds[var_name])\n", + "* name: `var.name`\n", + "* data content: `var.data`\n", + "* attributes: `var.attrs`\n", + "* dimenstions: `var.dims`\n", + "* data type: `var.data.dtype`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a632a525", + "metadata": {}, + "outputs": [], + "source": [ + "var_name = \"time\"\n", + "var = ds[var_name]\n", + "\n", + "print(\"var.name: \\n\", var.name, \"\\n\")\n", + "print(\"var.data: \\n\", var.data, \"\\n\")\n", + "print(\"var.attrs: \\n\", var.attrs, \"\\n\")\n", + "print(\"var.dims: \\n\", var.dims, \"\\n\")\n", + "print(\"var.data.dtype: \\n\", var.data.dtype, \"\\n\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b256b07f", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: indivdual variable info table (collect and display variable info as a tabular format)\n", + "df_info = pd.DataFrame()\n", + "df_info[\"var_name\"] = list(ds.variables)\n", + "df_info[\"dims\"] = df_info.var_name.apply(lambda x: ds[x].dims)\n", + "df_info[\"is_dim\"] = df_info.var_name.apply(lambda x: x in ds.dims)\n", + "df_info[\"n_dim\"] = df_info.var_name.apply(lambda x: len(ds[x].dims))\n", + "df_info[\"attrs\"] = df_info.var_name.apply(lambda x: ds[x].attrs)\n", + "df_info[\"dtype\"] = df_info.var_name.apply(lambda x: ds[x].data.dtype)\n", + "df_info" + ] + }, + { + "cell_type": "markdown", + "id": "e6a36b96", + "metadata": {}, + "source": [ + "### Data cleaning/Preprocessing (skipped)\n", + "Data cleaning and preprocessing is an important stage in the the data analysis pipeline. However it will be out of the scope of this notebook. For more information about data cleaning and preprocessing basics, here are some references. \n", + "* [Xarray Fundamentals](https://earth-env-data-science.github.io/lectures/xarray/xarray.html)\n", + "* [Xarray Tutorial — Pangeo Gallery documentation](http://gallery.pangeo.io/repos/pangeo-data/pangeo-tutorial-gallery/xarray.html)\n", + "* [Pythonic Data Cleaning With pandas and NumPy](https://realpython.com/python-data-cleaning-numpy-pandas/)\n", + "* [Pandas - Cleaning Data](https://www.w3schools.com/python/pandas/pandas_cleaning.asp)" + ] + }, + { + "cell_type": "markdown", + "id": "ae6e1a69", + "metadata": {}, + "source": [ + "### Plotting\n", + "Data visualization (or plotting) is another important data analysis topic and deserves its own discussion. Here in this notebook we will only demonstrate basic tool for simple data visualization tasks.\n", + "\n", + "Here are some reference you might find useful:\n", + "* xarray plotting: https://docs.xarray.dev/en/stable/user-guide/plotting.html\n", + "* Atmospheric Community Toolkit (ACT): https://arm-doe.github.io/ACT/index.html\n", + "\n", + "Note: this notebook is auto-generated using a template. It uses a general idea to select variable(s) to plot and is not customized to each indivdual notebook. Feel free to change the variables in intrrests, especially certain figure is failed to plot." + ] + }, + { + "cell_type": "markdown", + "id": "7e417fb2", + "metadata": {}, + "source": [ + "#### 1-dimenssional basic time series plot\n", + "\n", + "For the following plot we would like to find variables such that\n", + "* it has one and only one dimession\n", + "* \"time\" is its coordinate variable\n", + "* it is not a dimenssion itself,\n", + "* it is not a special variable with substrings within [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "\n", + "Please see the following example in action. (For more details about pandas filtering, please see the following references.)\n", + "* [pandas: multiple conditions while indexing data frame](https://stackoverflow.com/questions/22591174/pandas-multiple-conditions-while-indexing-data-frame-unexpected-behavior)\n", + "* [pandas.DataFrame.apply](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.apply.html)\n", + "* [How to test if a string contains one of the substrings in a list...](https://stackoverflow.com/questions/26577516/how-to-test-if-a-string-contains-one-of-the-substrings-in-a-list-in-pandas" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30edac2d", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==1, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter = df_info[(df_info.n_dim == 1) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4777a659", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_1d = df_filter.var_name.values[0]\n", + " var_1d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb5ae985", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " ds[var_1d].plot()\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "9e41066e", + "metadata": {}, + "source": [ + "#### 2-dimenssional basic plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09bd4adc", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==2, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_2 = df_info[(df_info.n_dim == 2) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2cdea1a9", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_2d = df_filter_2.var_name.values[0]\n", + " var_2d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5f48879", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " print(ds[var_2d].dims)\n", + " # ds[var_2d].plot()\n", + "\n", + " # conventionally, use \"time\" as x-axis\n", + " ds[var_2d].plot(x=\"time\")\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "7554b2a6", + "metadata": {}, + "source": [ + "#### qc-plotting (optional)\n", + "\n", + "Note: act qc plotting has more strict requirement, one of them is the associated qc variable need to have \"flag_masks\" attributes. Which is added by using ds = act.io.armfiles.read_netcdf(files_list), then ds.clean.cleanup()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71c2096f", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " ds_act = act.io.armfiles.read_netcdf(full_path)\n", + " print(type(ds_act))\n", + " ds_act.clean.cleanup()\n", + "\n", + " # or \n", + " # ds.clean.cleanup()\n", + "except Exception as e:\n", + " print(\"ERROR\", e)\n", + " ds_act = ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "844f8505", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter valid variables for ACT qc plotting\n", + "condition = (\"qc_\" + df_info.var_name).apply(lambda x: ds_act[x].attrs.get(\"flag_masks\") is not None \n", + " if x in list(ds.variables) else False)\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_3 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings))) &\n", + " condition\n", + " ]\n", + "df_filter_3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd38cc6", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "try:\n", + " qc_variable = df_filter_3.var_name.values[0]\n", + " print(qc_variable)\n", + "except Exception as e:\n", + " print(e)\n", + " \n", + "try:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + " qc_ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "d76e4d27", + "metadata": {}, + "source": [ + "#### bonus: choose variables to plot from a dropdown menu " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb733804", + "metadata": {}, + "outputs": [], + "source": [ + "# Valid variables filtering\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_4 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82d5c20d", + "metadata": {}, + "outputs": [], + "source": [ + "# example 1: using xarray plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2fc9aaa4", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# fig, ax = plt.subplots(figsize=(10, 4))\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "# fig.clear() # Remove old lines from plot and plot new one\n", + "# if len(ds[var].dims)==2:\n", + "# ds[var].plot(x=\"time\", add_colorbar=False)\n", + "# else:\n", + "# ds[var].plot()\n", + "# plt.grid()\n", + "# plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fabdd802", + "metadata": {}, + "outputs": [], + "source": [ + "# example 2: using act plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "962b4186", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "\n", + "\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "\n", + "# i_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + "# i_display.add_subplots((1,), figsize=(10, 4))\n", + "# ax = i_display.plot(var, subplot_index=(0,), set_title=f\"{var} ({ds_act[var].attrs['long_name']})\",)\n", + "\n", + "# ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "# ax.grid()\n", + "# plt.show()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/AOSSP2BC/aossp2rbc1m.c1.ipynb b/VAPs/quicklook/AOSSP2BC/aossp2rbc1m.c1.ipynb new file mode 100644 index 00000000..ab355710 --- /dev/null +++ b/VAPs/quicklook/AOSSP2BC/aossp2rbc1m.c1.ipynb @@ -0,0 +1,339 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# AOSSP2RBC1M.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/aossp2bc) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'aossp2rbc1m'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2014-10-19', 'facility': 'S1', 'site': 'mao', 'start_date': '2014-02-17'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'mao', 'S1' )\n", + "\n", + "date_start = '2014-10-17'\n", + "date_end = '2014-10-19'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['rBC', 'N_dN_rBC', 'N_dN_pure_scattering']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'rBC'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'rBC'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/ARMBE/ARMBE_tutorial.ipynb b/VAPs/quicklook/ARMBE/ARMBE_tutorial.ipynb new file mode 100644 index 00000000..a74da809 --- /dev/null +++ b/VAPs/quicklook/ARMBE/ARMBE_tutorial.ipynb @@ -0,0 +1,838 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# ARMBEATM.C1 Notebook\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/armbe) for more information about this vap." + ] + }, + { + "cell_type": "markdown", + "id": "97097763", + "metadata": {}, + "source": [ + "In this notebook, we demonstrate the workflow to explore ARM vap data (using armbeatm as an example.) Value-added products (VAPs) are higher-order data products that have been analyzed and processed to ease scientists’ use of ARM data in atmospheric research and global climate models. \n", + "Here is the main content we will cover." + ] + }, + { + "cell_type": "markdown", + "id": "eddec40f", + "metadata": {}, + "source": [ + "# Table of Content\n", + "## Access the data\n", + "* How to retrieve the data\n", + "* Data path and file name conventions\n", + "* Load data\n", + "## Explore the data\n", + "* NetCDF Data structure\n", + "* Xarray essentials\n", + "* Xarray Variable\n", + "## Plot the data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "import random\n", + "\n", + "import glob\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "2372d5be", + "metadata": {}, + "source": [ + "## Access the data" + ] + }, + { + "cell_type": "markdown", + "id": "5b0d9684", + "metadata": {}, + "source": [ + "### How to retrieve the data\n", + "We assume the path \"/data/archive\" is available where you are running this notebook. You can use `os.path.exists(\"/data/archive\")` to verify if the path exists at your machine.\n", + "\n", + "\n", + "### Data path and file name conventions\n", + "There several common terminologies regarding ARM data, for example, data-stream-name, data-level, site, facility, instrument, etc. (For more details, please see [ARM Data File Standards Version 1.2](https://www.google.com/search?q=arm+datastream+facility+cite+definition&rlz=1C1GCEJ_enUS1029US1029&ei=hb41ZICrBPukqtsPvMODIA&ved=0ahUKEwjAgeiS0aL-AhV7kmoFHbzhAAQQ4dUDCBA&uact=5&oq=arm+datastream+facility+cite+definition&gs_lcp=Cgxnd3Mtd2l6LXNlcnAQAzIFCAAQogQyBQgAEKIEMgUIABCiBDIFCAAQogQyBQgAEKIEOgoIABBHENYEELADOgoIIRCgARDDBBAKSgQIQRgAUJ4DWIoOYO0PaAFwAXgAgAGMAYgBuQmSAQMyLjmYAQCgAQHIAQjAAQE&sclient=gws-wiz-serp).) \n", + "\n", + "For example, this notebook is called `armbeatm.c1`, where `armbeatm` is the \"datastream name\", and the `{process.ds_class_level}}` is the \"data level\".\n", + "\n", + "This datastream also contains site `cor` and facility `M1`. (Note: individual datastream might have multiple site-facility pairs.)\n", + "In such a case, the data of this data-stream is stored at `/data/archive/cor/corarmbeatmM1.c1`, which is in the format of `//.`. We can use the following method to assign the data-stream directory `datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )`\n", + "\n", + "The data files under datastream_dir also follows naming conventions. But once reach the datastream_dir level, the most import file naming convention to differentiate the files is \"yyyyMMdd.hhmmss\", which comes handy to filter out files based on datetime. For example, we can use `glob.glob(f'{datastream_dir}/*.200709*.*')` to filter files in 2007 September.\n", + "\n", + "(Note: refer to the https://adc.arm.gov/discovery/#/ and https://adc.arm.gov/solr8/metadata/select API to explore ARM datastream and assoicated available sites and facilities\n", + "\n", + "Please see the following examples in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d7e9eb85", + "metadata": {}, + "outputs": [], + "source": [ + "# Verify if DATA_DIR path exists\n", + "DATA_DIR = \"/data/archive\"\n", + "os.path.exists(DATA_DIR)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "586993fd", + "metadata": {}, + "outputs": [], + "source": [ + "# Speicify datastream_dir following the path conventions and check its existence\n", + "DATASTREAM_NAME = \"armbeatm\"\n", + "DATA_LEVEL = \"c1\"\n", + "site = \"cor\"\n", + "facility = \"M1\"\n", + "datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )\n", + "print(datastream_dir)\n", + "print(os.path.exists(datastream_dir))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0742f7c1", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: list 5 (random) files under datastream_dir\n", + "files = os.listdir(datastream_dir)\n", + "files[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39b98a36", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: get most recent file\n", + "list_of_files = glob.glob(f\"{datastream_dir}/*\") # * means all if need specific format then *.csv\n", + "latest_file = max(list_of_files, key=os.path.getctime)\n", + "latest_file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "902d514e", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus sort datastream files based on datetime\n", + "files = os.listdir(datastream_dir)\n", + "file_sorted = files.copy()\n", + "file_sorted.sort() \n", + "print(file_sorted[:5])\n", + "\n", + "# to reverse\n", + "file_sorted_reverse = files.copy() \n", + "file_sorted_reverse.sort(reverse=True)\n", + "print(file_sorted_reverse[:5])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec5923b2", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: pattern matching\n", + "# filter the 200709** files under datastream_dir\n", + "files_filter = glob.glob(f'{datastream_dir}/*.200709*.*')\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c4a4aa18", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: use armnotebook_utils.file_filter (TODO) to filter files based on datastream info \n" + ] + }, + { + "cell_type": "markdown", + "id": "cfeb9efc", + "metadata": {}, + "source": [ + "### Load data\n", + "The arm data is stored in [NetCDF](https://en.wikipedia.org/wiki/NetCDF#:~:text=%22NetCDF%20(network%20Common%20Data%20Form,format%20for%20representing%20scientific%20data.) format. We can use xarray's `open_dataset` method to load single file, or `open_mfdataset` to open multiple files. (Note: the latter will still return a single xarray dataset object by combining multiple files.)\n", + "\n", + "Note: open_dataset keeps the file handle open and lazy loads its contents. All parameters are passed directly to open_dataset. It is a preferable method over load_dataset for memory-effiency. (more details, please see [xarray.open_dataset](https://docs.xarray.dev/en/stable/generated/xarray.open_dataset.html))\n", + "\n", + "See the following example in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a440a329", + "metadata": {}, + "outputs": [], + "source": [ + "# open single file\n", + "full_path = latest_file\n", + "print(full_path)\n", + "ds_single = xr.open_dataset(full_path)\n", + "ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0143a3d", + "metadata": {}, + "outputs": [], + "source": [ + "print(ds_single)\n", + "print(type(ds_single))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c0f8939", + "metadata": {}, + "outputs": [], + "source": [ + "# open multiple files\n", + "n_files = 3\n", + "full_paths = [os.path.join(datastream_dir, f_path) for f_path in file_sorted_reverse[:n_files]]\n", + "print(full_paths)\n", + "try: # Note: sometimes multiple files cannot be merged, so we used try except here.\n", + " ds_mutiple = xr.open_mfdataset(full_paths)\n", + " # ds_mutiple\n", + " print(type(ds_mutiple))\n", + " print(ds_mutiple)\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "3fa59943", + "metadata": {}, + "source": [ + "## Explore the data" + ] + }, + { + "cell_type": "markdown", + "id": "571f69b9", + "metadata": {}, + "source": [ + "### NetCDF Data structure \n", + "\n", + "\n", + "(If you are confident with NetCDF basics and xarray essentials, feel free to skip this session.)\n", + "Before we dive into data exploration, there are some eseential concpets we should be familiar with (shown below): \n", + "* Dataset\n", + "* Data array\n", + "* Variable\n", + "* Dimenssion\n", + "* Coordinate\n", + "* Data Type\n", + "* Meta Data (Attributes)\n", + "\n", + "We will not go into details about NetCDF basics and here are some references you might find helpful\n", + "* [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#)\n", + "* [Network Common Data Form (NetCDF)](https://www.unidata.ucar.edu/software/netcdf/) and [A Brief History of (netCDF) Time](https://www.unidata.ucar.edu/software/netcdf/time/recs.html)\n", + "\n", + "\n", + "\n", + "### Xarray essentials\n", + "Earlier we introduced the xarray pacakge and used `open_dataset` and `open_mfdataset` to retrieve NetCDF data file as an xarray Dataset object. Recall that you can review the data in a notebook by using `print(ds)` or `ds`. \n", + "\n", + "Using xarray to retrieve the aforementioned NetCDF basics is straightforwared. In general,\n", + "* Dataset: `ds`\n", + "* Data array: `ds.variables`\n", + "* Variable: `ds.ds.variables`\n", + "* Dimenssion: `ds.dims`\n", + "* Coordinate: `ds.coords`\n", + "* Data Type: `type`\n", + "* Meta Data (Attributes)\n", + "\n", + "Also, here are some references if you are new to xarray\n", + "* [Xarray in 45 minutes](https://tutorial.xarray.dev/overview/xarray-in-45-min.html)\n", + "* [Handling NetCDF Files using XArray for Absolute Beginners](https://towardsdatascience.com/handling-netcdf-files-using-xarray-for-absolute-beginners-111a8ab4463f)\n", + "\n", + "Try the following commands in action\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b3eac323", + "metadata": {}, + "outputs": [], + "source": [ + "ds = ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77ecf85d", + "metadata": {}, + "outputs": [], + "source": [ + "# Dataset \n", + "ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25e7de09", + "metadata": {}, + "outputs": [], + "source": [ + "# Data array, Variable\n", + "# Note: the info can be overwhelming. Do not attempt to grasp everything at the first glance.\n", + "ds.variables" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c41b67e", + "metadata": {}, + "outputs": [], + "source": [ + "# dimenssions\n", + "ds.dims" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "156f1dfc", + "metadata": {}, + "outputs": [], + "source": [ + "# coordinates\n", + "ds.coords" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "277d6064", + "metadata": {}, + "outputs": [], + "source": [ + "# Meta Data (Attributes)\n", + "ds.attrs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d334681f", + "metadata": {}, + "outputs": [], + "source": [ + "# type\n", + "print(type(ds))\n", + "print(type(ds.variables))\n", + "print(type(ds.dims))\n", + "print(type(ds.coords))\n", + "print(type(ds.attrs))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "643399d6", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: retrieve variable names only\n", + "list(ds.variables)" + ] + }, + { + "cell_type": "markdown", + "id": "1cf36878", + "metadata": {}, + "source": [ + "#### Discussion: variable vs. coordinates vs. dimenssions. \n", + "From [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#):\n", + "\n", + "* Dimession: A dimension may be used to represent a real physical dimension, for example, time, latitude, longitude, or height. A dimension might also be used to index other quantities, for example station or model-run-number.\n", + "A netCDF dimension has both a name and a length. A dimension length is an arbitrary positive integer, except that one dimension in a netCDF dataset can have the length UNLIMITED.\n", + "\n", + "* Variables: Variables are used to store the bulk of the data in a netCDF dataset. A variable represents an array of values of the same type. A scalar value is treated as a 0-dimensional array. A variable has a name, a data type, and a shape described by its list of dimensions specified when the variable is created. A variable may also have associated attributes, which may be added, deleted or changed after the variable is created.\n", + "\n", + "* Coordinate (Variables): It is legal for a variable to have the same name as a dimension. Such variables have no special meaning to the netCDF library. However there is a convention that such variables should be treated in a special way by software using this library.\n", + "A variable with the same name as a dimension is called a coordinate variable. It typically defines a physical coordinate corresponding to that dimension.\n", + "\n", + "\n", + "Tips: By definition, coordinates and dimenssions are also variable. For example ds.time is a coordinate but it is also a special variable. But in practice, when we talk about variable, it implies regular/non-coordinate variable. We will use this convention for the remaining notebook. " + ] + }, + { + "cell_type": "markdown", + "id": "d47120f9", + "metadata": {}, + "source": [ + "### Xarray Variable\n", + "\n", + "\n", + "We can use `ds.variables` to access varialbes of a dataset. But many times viewing print-out the whole datasets can be overwhemling. Instead, we would work on the individual variable (also called Xarray data array.) using the `ds[var_name]` syntax, where `var_name` is the name of the variable. See the following examples in action.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5a57e136", + "metadata": {}, + "outputs": [], + "source": [ + "print(type(ds[\"time\"]))\n", + "ds[\"time\"]" + ] + }, + { + "cell_type": "markdown", + "id": "51ec643e", + "metadata": {}, + "source": [ + "#### Variable properties\n", + "For individual varible, the following properties are mostly used with the assocaited xarray method to retrieve them. (assuming var = ds[var_name])\n", + "* name: `var.name`\n", + "* data content: `var.data`\n", + "* attributes: `var.attrs`\n", + "* dimenstions: `var.dims`\n", + "* data type: `var.data.dtype`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a632a525", + "metadata": {}, + "outputs": [], + "source": [ + "var_name = \"time\"\n", + "var = ds[var_name]\n", + "\n", + "print(\"var.name: \\n\", var.name, \"\\n\")\n", + "print(\"var.data: \\n\", var.data, \"\\n\")\n", + "print(\"var.attrs: \\n\", var.attrs, \"\\n\")\n", + "print(\"var.dims: \\n\", var.dims, \"\\n\")\n", + "print(\"var.data.dtype: \\n\", var.data.dtype, \"\\n\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b256b07f", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: indivdual variable info table (collect and display variable info as a tabular format)\n", + "df_info = pd.DataFrame()\n", + "df_info[\"var_name\"] = list(ds.variables)\n", + "df_info[\"dims\"] = df_info.var_name.apply(lambda x: ds[x].dims)\n", + "df_info[\"is_dim\"] = df_info.var_name.apply(lambda x: x in ds.dims)\n", + "df_info[\"n_dim\"] = df_info.var_name.apply(lambda x: len(ds[x].dims))\n", + "df_info[\"attrs\"] = df_info.var_name.apply(lambda x: ds[x].attrs)\n", + "df_info[\"dtype\"] = df_info.var_name.apply(lambda x: ds[x].data.dtype)\n", + "df_info" + ] + }, + { + "cell_type": "markdown", + "id": "e6a36b96", + "metadata": {}, + "source": [ + "### Data cleaning/Preprocessing (skipped)\n", + "Data cleaning and preprocessing is an important stage in the the data analysis pipeline. However it will be out of the scope of this notebook. For more information about data cleaning and preprocessing basics, here are some references. \n", + "* [Xarray Fundamentals](https://earth-env-data-science.github.io/lectures/xarray/xarray.html)\n", + "* [Xarray Tutorial — Pangeo Gallery documentation](http://gallery.pangeo.io/repos/pangeo-data/pangeo-tutorial-gallery/xarray.html)\n", + "* [Pythonic Data Cleaning With pandas and NumPy](https://realpython.com/python-data-cleaning-numpy-pandas/)\n", + "* [Pandas - Cleaning Data](https://www.w3schools.com/python/pandas/pandas_cleaning.asp)" + ] + }, + { + "cell_type": "markdown", + "id": "ae6e1a69", + "metadata": {}, + "source": [ + "### Plotting\n", + "Data visualization (or plotting) is another important data analysis topic and deserves its own discussion. Here in this notebook we will only demonstrate basic tool for simple data visualization tasks.\n", + "\n", + "Here are some reference you might find useful:\n", + "* xarray plotting: https://docs.xarray.dev/en/stable/user-guide/plotting.html\n", + "* Atmospheric Community Toolkit (ACT): https://arm-doe.github.io/ACT/index.html\n", + "\n", + "Note: this notebook is auto-generated using a template. It uses a general idea to select variable(s) to plot and is not customized to each indivdual notebook. Feel free to change the variables in intrrests, especially certain figure is failed to plot." + ] + }, + { + "cell_type": "markdown", + "id": "7e417fb2", + "metadata": {}, + "source": [ + "#### 1-dimenssional basic time series plot\n", + "\n", + "For the following plot we would like to find variables such that\n", + "* it has one and only one dimession\n", + "* \"time\" is its coordinate variable\n", + "* it is not a dimenssion itself,\n", + "* it is not a special variable with substrings within [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "\n", + "Please see the following example in action. (For more details about pandas filtering, please see the following references.)\n", + "* [pandas: multiple conditions while indexing data frame](https://stackoverflow.com/questions/22591174/pandas-multiple-conditions-while-indexing-data-frame-unexpected-behavior)\n", + "* [pandas.DataFrame.apply](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.apply.html)\n", + "* [How to test if a string contains one of the substrings in a list...](https://stackoverflow.com/questions/26577516/how-to-test-if-a-string-contains-one-of-the-substrings-in-a-list-in-pandas" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30edac2d", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==1, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter = df_info[(df_info.n_dim == 1) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4777a659", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_1d = df_filter.var_name.values[0]\n", + " var_1d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb5ae985", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " ds[var_1d].plot()\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "9e41066e", + "metadata": {}, + "source": [ + "#### 2-dimenssional basic plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09bd4adc", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==2, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_2 = df_info[(df_info.n_dim == 2) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2cdea1a9", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_2d = df_filter_2.var_name.values[0]\n", + " var_2d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5f48879", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " print(ds[var_2d].dims)\n", + " # ds[var_2d].plot()\n", + "\n", + " # conventionally, use \"time\" as x-axis\n", + " ds[var_2d].plot(x=\"time\")\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "7554b2a6", + "metadata": {}, + "source": [ + "#### qc-plotting (optional)\n", + "\n", + "Note: act qc plotting has more strict requirement, one of them is the associated qc variable need to have \"flag_masks\" attributes. Which is added by using ds = act.io.armfiles.read_netcdf(files_list), then ds.clean.cleanup()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71c2096f", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " ds_act = act.io.armfiles.read_netcdf(full_path)\n", + " print(type(ds_act))\n", + " ds_act.clean.cleanup()\n", + "\n", + " # or \n", + " # ds.clean.cleanup()\n", + "except Exception as e:\n", + " print(\"ERROR\", e)\n", + " ds_act = ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "844f8505", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter valid variables for ACT qc plotting\n", + "condition = (\"qc_\" + df_info.var_name).apply(lambda x: ds_act[x].attrs.get(\"flag_masks\") is not None \n", + " if x in list(ds.variables) else False)\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_3 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings))) &\n", + " condition\n", + " ]\n", + "df_filter_3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd38cc6", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "try:\n", + " qc_variable = df_filter_3.var_name.values[0]\n", + " print(qc_variable)\n", + "except Exception as e:\n", + " print(e)\n", + " \n", + "try:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + " qc_ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "d76e4d27", + "metadata": {}, + "source": [ + "#### bonus: choose variables to plot from a dropdown menu " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb733804", + "metadata": {}, + "outputs": [], + "source": [ + "# Valid variables filtering\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_4 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82d5c20d", + "metadata": {}, + "outputs": [], + "source": [ + "# example 1: using xarray plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2fc9aaa4", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# fig, ax = plt.subplots(figsize=(10, 4))\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "# fig.clear() # Remove old lines from plot and plot new one\n", + "# if len(ds[var].dims)==2:\n", + "# ds[var].plot(x=\"time\", add_colorbar=False)\n", + "# else:\n", + "# ds[var].plot()\n", + "# plt.grid()\n", + "# plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fabdd802", + "metadata": {}, + "outputs": [], + "source": [ + "# example 2: using act plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "962b4186", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "\n", + "\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "\n", + "# i_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + "# i_display.add_subplots((1,), figsize=(10, 4))\n", + "# ax = i_display.plot(var, subplot_index=(0,), set_title=f\"{var} ({ds_act[var].attrs['long_name']})\",)\n", + "\n", + "# ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "# ax.grid()\n", + "# plt.show()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/ARMBE/armbeatm.c1.ipynb b/VAPs/quicklook/ARMBE/armbeatm.c1.ipynb new file mode 100644 index 00000000..4716a81d --- /dev/null +++ b/VAPs/quicklook/ARMBE/armbeatm.c1.ipynb @@ -0,0 +1,297 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# ARMBEATM.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/armbe) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'armbeatm'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2019-04-30', 'facility': 'M1', 'site': 'cor', 'start_date': '2018-09-23'}, {'end_date': '2016-12-31', 'facility': 'M1', 'site': 'awr', 'start_date': '2016-01-01'}, {'end_date': '2020-05-31', 'facility': 'M1', 'site': 'anx', 'start_date': '2019-12-01'}, {'end_date': '2006-12-30', 'facility': 'M1', 'site': 'nim', 'start_date': '2006-01-01'}, {'end_date': '2021-06-14', 'facility': 'M1', 'site': 'oli', 'start_date': '2013-10-01'}, {'end_date': '2022-09-30', 'facility': 'M1', 'site': 'hou', 'start_date': '2021-10-01'}, {'end_date': '2020-12-31', 'facility': 'C1', 'site': 'ena', 'start_date': '2014-01-01'}, {'end_date': '2015-12-31', 'facility': 'M1', 'site': 'mao', 'start_date': '2014-01-01'}, {'end_date': '2023-06-15', 'facility': 'M1', 'site': 'guc', 'start_date': '2021-09-01'}, {'end_date': '2020-12-31', 'facility': 'C1', 'site': 'nsa', 'start_date': '2001-01-01'}, {'end_date': '2020-12-27', 'facility': 'C1', 'site': 'sgp', 'start_date': '1994-01-01'}, {'end_date': '2010-12-31', 'facility': 'C1', 'site': 'twp', 'start_date': '1996-01-01'}, {'end_date': '2010-12-31', 'facility': 'C2', 'site': 'twp', 'start_date': '1998-01-01'}, {'end_date': '2010-12-31', 'facility': 'C3', 'site': 'twp', 'start_date': '2002-01-01'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2019-12-30'\n", + "date_end = '2020-01-01'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['u_wind_sfc', 'v_wind_sfc', 'relative_humidity_sfc']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'u_wind_sfc'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/ARMBE/armbecldrad.c1.ipynb b/VAPs/quicklook/ARMBE/armbecldrad.c1.ipynb new file mode 100644 index 00000000..0c3d67bb --- /dev/null +++ b/VAPs/quicklook/ARMBE/armbecldrad.c1.ipynb @@ -0,0 +1,339 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# ARMBECLDRAD.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/armbe) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'armbecldrad'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2016-12-31', 'facility': 'M1', 'site': 'awr', 'start_date': '2016-01-01'}, {'end_date': '2020-05-31', 'facility': 'M1', 'site': 'anx', 'start_date': '2019-12-01'}, {'end_date': '2006-12-30', 'facility': 'M1', 'site': 'nim', 'start_date': '2006-01-01'}, {'end_date': '2020-12-31', 'facility': 'C1', 'site': 'nsa', 'start_date': '1998-01-01'}, {'end_date': '2020-12-31', 'facility': 'C1', 'site': 'ena', 'start_date': '2014-01-01'}, {'end_date': '2022-09-30', 'facility': 'M1', 'site': 'hou', 'start_date': '2021-10-01'}, {'end_date': '2015-12-01', 'facility': 'M1', 'site': 'mao', 'start_date': '2014-01-01'}, {'end_date': '2023-06-15', 'facility': 'M1', 'site': 'guc', 'start_date': '2021-09-01'}, {'end_date': '2019-04-30', 'facility': 'M1', 'site': 'cor', 'start_date': '2018-09-23'}, {'end_date': '2020-10-31', 'facility': 'M1', 'site': 'mos', 'start_date': '2019-11-01'}, {'end_date': '2021-06-14', 'facility': 'M1', 'site': 'oli', 'start_date': '2013-10-01'}, {'end_date': '2020-12-31', 'facility': 'C1', 'site': 'sgp', 'start_date': '1996-01-01'}, {'end_date': '2011-12-31', 'facility': 'C1', 'site': 'twp', 'start_date': '1996-01-01'}, {'end_date': '2010-12-31', 'facility': 'C2', 'site': 'twp', 'start_date': '1998-01-01'}, {'end_date': '2011-12-31', 'facility': 'C3', 'site': 'twp', 'start_date': '2002-01-01'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2019-12-30'\n", + "date_end = '2020-01-01'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['cld_frac', 'tot_cld', 'swdn']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'cld_frac'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'cld_frac'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/ARSCL/.ipynb_checkpoints/arscl1cloth.c1-checkpoint.ipynb b/VAPs/quicklook/ARSCL/.ipynb_checkpoints/arscl1cloth.c1-checkpoint.ipynb new file mode 100644 index 00000000..4502aea5 --- /dev/null +++ b/VAPs/quicklook/ARSCL/.ipynb_checkpoints/arscl1cloth.c1-checkpoint.ipynb @@ -0,0 +1,2631 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# ARSCL1CLOTH.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/arscl) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'arscl1cloth'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2011-03-23', 'facility': 'C1', 'site': 'nsa', 'start_date': '1998-03-25'}, {'end_date': '2011-01-04', 'facility': 'C1', 'site': 'sgp', 'start_date': '1996-11-08'}, {'end_date': '2011-03-07', 'facility': 'C1', 'site': 'twp', 'start_date': '1999-07-01'}, {'end_date': '2009-02-14', 'facility': 'C2', 'site': 'twp', 'start_date': '1998-11-01'}, {'end_date': '2011-02-28', 'facility': 'C3', 'site': 'twp', 'start_date': '2003-01-01'}]" + ] + }, + { + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "ac6764f5", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The following locations and date ranges are available for this VAP:\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
sitefacilitystart_dateend_date
0nsaC11998-03-252011-03-23
1sgpC11996-11-082011-01-04
2twpC11999-07-012011-03-07
3twpC21998-11-012009-02-14
4twpC32003-01-012011-02-28
\n", + "
" + ], + "text/plain": [ + " site facility start_date end_date\n", + "0 nsa C1 1998-03-25 2011-03-23\n", + "1 sgp C1 1996-11-08 2011-01-04\n", + "2 twp C1 1999-07-01 2011-03-07\n", + "3 twp C2 1998-11-01 2009-02-14\n", + "4 twp C3 2003-01-01 2011-02-28" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2011-01-03'\n", + "date_end = '2011-01-04'" + ] + }, + { + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'/data/archive/sgp/sgparscl1clothC1.c1'" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['20110103', '20110104']" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "51feea2e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['/data/archive/sgp/sgparscl1clothC1.c1/sgparscl1clothC1.c1.20110103.000000.cdf',\n", + " '/data/archive/sgp/sgparscl1clothC1.c1/sgparscl1clothC1.c1.20110104.000000.cdf']" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "77 files loaded\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
<xarray.Dataset>\n",
+       "Dimensions:                         (time: 8640, nheights: 512, numlayers: 10)\n",
+       "Coordinates:\n",
+       "  * time                            (time) timedelta64[ns] 00:00:00 ... 23:59:50\n",
+       "Dimensions without coordinates: nheights, numlayers\n",
+       "Data variables: (12/23)\n",
+       "    base_time                       object ...\n",
+       "    time_offset                     (time) timedelta64[ns] dask.array<chunksize=(8640,), meta=np.ndarray>\n",
+       "    Heights                         (nheights) float32 dask.array<chunksize=(512,), meta=np.ndarray>\n",
+       "    Reflectivity                    (time, nheights) int16 dask.array<chunksize=(8640, 512), meta=np.ndarray>\n",
+       "    ReflectivityNoClutter           (time, nheights) int16 dask.array<chunksize=(8640, 512), meta=np.ndarray>\n",
+       "    ReflectivityBestEstimate        (time, nheights) int16 dask.array<chunksize=(8640, 512), meta=np.ndarray>\n",
+       "    ...                              ...\n",
+       "    CloudLayerBottomHeightMplZwang  (time, numlayers) float32 dask.array<chunksize=(8640, 10), meta=np.ndarray>\n",
+       "    CloudLayerTopHeightMplZwang     (time, numlayers) float32 dask.array<chunksize=(8640, 10), meta=np.ndarray>\n",
+       "    qc_RadarArtifacts               (time, nheights) |S1 dask.array<chunksize=(8640, 512), meta=np.ndarray>\n",
+       "    qc_ReflectivityClutterFlag      (time, nheights) |S1 dask.array<chunksize=(8640, 512), meta=np.ndarray>\n",
+       "    qc_CloudLayerTopHeightMplZwang  (time, numlayers) float32 dask.array<chunksize=(8640, 10), meta=np.ndarray>\n",
+       "    qc_BeamAttenuationMplZwang      (time) float32 dask.array<chunksize=(8640,), meta=np.ndarray>\n",
+       "Attributes: (12/18)\n",
+       "    Date:                      Wed Jul 13 16:28:55 GMT 2011\n",
+       "    Version:                   $State: Exp $\n",
+       "    Number_Input_Platforms:    3\n",
+       "    Input_Platforms:           sgp30smplcmask1zwangC1.c1,sgpvceil25kC1.b1,sgp...\n",
+       "    Input_Platforms_Versions:  ?????,10.2,1.16\n",
+       "    Command_Line:              arsc1/arscl2 -s YYYYMMDD -e YYYYMMDD SITE FACI...\n",
+       "    ...                        ...\n",
+       "    commentf:                  Note that -32768 is also used for the geophysi...\n",
+       "    _file_dates:               ['20110103']\n",
+       "    _file_times:               ['000000']\n",
+       "    datastream:                sgparscl1clothC1.c1\n",
+       "    _datastream:               sgparscl1clothC1.c1\n",
+       "    _arm_standards_flag:       1
" + ], + "text/plain": [ + "\n", + "Dimensions: (time: 8640, nheights: 512, numlayers: 10)\n", + "Coordinates:\n", + " * time (time) timedelta64[ns] 00:00:00 ... 23:59:50\n", + "Dimensions without coordinates: nheights, numlayers\n", + "Data variables: (12/23)\n", + " base_time object ...\n", + " time_offset (time) timedelta64[ns] dask.array\n", + " Heights (nheights) float32 dask.array\n", + " Reflectivity (time, nheights) int16 dask.array\n", + " ReflectivityNoClutter (time, nheights) int16 dask.array\n", + " ReflectivityBestEstimate (time, nheights) int16 dask.array\n", + " ... ...\n", + " CloudLayerBottomHeightMplZwang (time, numlayers) float32 dask.array\n", + " CloudLayerTopHeightMplZwang (time, numlayers) float32 dask.array\n", + " qc_RadarArtifacts (time, nheights) |S1 dask.array\n", + " qc_ReflectivityClutterFlag (time, nheights) |S1 dask.array\n", + " qc_CloudLayerTopHeightMplZwang (time, numlayers) float32 dask.array\n", + " qc_BeamAttenuationMplZwang (time) float32 dask.array\n", + "Attributes: (12/18)\n", + " Date: Wed Jul 13 16:28:55 GMT 2011\n", + " Version: $State: Exp $\n", + " Number_Input_Platforms: 3\n", + " Input_Platforms: sgp30smplcmask1zwangC1.c1,sgpvceil25kC1.b1,sgp...\n", + " Input_Platforms_Versions: ?????,10.2,1.16\n", + " Command_Line: arsc1/arscl2 -s YYYYMMDD -e YYYYMMDD SITE FACI...\n", + " ... ...\n", + " commentf: Note that -32768 is also used for the geophysi...\n", + " _file_dates: ['20110103']\n", + " _file_times: ['000000']\n", + " datastream: sgparscl1clothC1.c1\n", + " _datastream: sgparscl1clothC1.c1\n", + " _arm_standards_flag: 1" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter[0]\n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['Reflectivity', 'ReflectivityNoClutter', 'ReflectivityBestEstimate']" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "de5b8b3d", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
<xarray.DataArray 'ReflectivityNoClutter' (time: 8640, nheights: 512)>\n",
+       "dask.array<open_dataset-86c2f04dd636517ce84998ed679d9bf5ReflectivityNoClutter, shape=(8640, 512), dtype=int16, chunksize=(8640, 512), chunktype=numpy.ndarray>\n",
+       "Coordinates:\n",
+       "  * time     (time) timedelta64[ns] 00:00:00 00:00:10 ... 23:59:40 23:59:50\n",
+       "Dimensions without coordinates: nheights\n",
+       "Attributes:\n",
+       "    long_name:  MMCR Reflectivity with Clutter Removed\n",
+       "    units:      dBZ (X100)\n",
+       "    comment:    Divide ReflectivityNoClutter by 100 to get dBZ
" + ], + "text/plain": [ + "\n", + "dask.array\n", + "Coordinates:\n", + " * time (time) timedelta64[ns] 00:00:00 00:00:10 ... 23:59:40 23:59:50\n", + "Dimensions without coordinates: nheights\n", + "Attributes:\n", + " long_name: MMCR Reflectivity with Clutter Removed\n", + " units: dBZ (X100)\n", + " comment: Divide ReflectivityNoClutter by 100 to get dBZ" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ds.ReflectivityNoClutter.data" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [ + { + "ename": "UFuncTypeError", + "evalue": "Cannot cast ufunc 'greater_equal' input 0 from dtype(' 5\u001b[0m ts_ax \u001b[38;5;241m=\u001b[39m \u001b[43mts_display\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mplot\u001b[49m\u001b[43m(\u001b[49m\u001b[43mv\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43msubplot_index\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mi\u001b[49m\u001b[43m,\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mset_title\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mds\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mvariables\u001b[49m\u001b[43m[\u001b[49m\u001b[43mv\u001b[49m\u001b[43m]\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mattrs\u001b[49m\u001b[43m[\u001b[49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43mlong_name\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m,\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 6\u001b[0m ts_ax\u001b[38;5;241m.\u001b[39mgrid()\n\u001b[1;32m 8\u001b[0m plt\u001b[38;5;241m.\u001b[39mshow()\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/act/plotting/timeseriesdisplay.py:588\u001b[0m, in \u001b[0;36mTimeSeriesDisplay.plot\u001b[0;34m(self, field, dsname, subplot_index, cmap, set_title, add_nan, day_night_background, invert_y_axis, abs_limits, time_rng, y_rng, use_var_for_y, set_shading, assessment_overplot, overplot_marker, overplot_behind, overplot_markersize, assessment_overplot_category, assessment_overplot_category_color, force_line_plot, labels, cbar_label, cbar_h_adjust, secondary_y, y_axis_flag_meanings, colorbar_labels, cb_friendly, **kwargs)\u001b[0m\n\u001b[1;32m 586\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124medgecolors\u001b[39m\u001b[38;5;124m'\u001b[39m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;129;01min\u001b[39;00m kwargs\u001b[38;5;241m.\u001b[39mkeys():\n\u001b[1;32m 587\u001b[0m kwargs[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124medgecolors\u001b[39m\u001b[38;5;124m'\u001b[39m] \u001b[38;5;241m=\u001b[39m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mface\u001b[39m\u001b[38;5;124m'\u001b[39m\n\u001b[0;32m--> 588\u001b[0m mesh \u001b[38;5;241m=\u001b[39m \u001b[43max\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mpcolormesh\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 589\u001b[0m \u001b[43m \u001b[49m\u001b[43mnp\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43masarray\u001b[49m\u001b[43m(\u001b[49m\u001b[43mxdata\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 590\u001b[0m \u001b[43m \u001b[49m\u001b[43mydata\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 591\u001b[0m \u001b[43m \u001b[49m\u001b[43mdata\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mtranspose\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 592\u001b[0m \u001b[43m \u001b[49m\u001b[43mshading\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mset_shading\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 593\u001b[0m \u001b[43m \u001b[49m\u001b[43mcmap\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcmap\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 594\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 595\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 597\u001b[0m \u001b[38;5;66;03m# Set Title\u001b[39;00m\n\u001b[1;32m 598\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m set_title \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/matplotlib/__init__.py:1442\u001b[0m, in \u001b[0;36m_preprocess_data..inner\u001b[0;34m(ax, data, *args, **kwargs)\u001b[0m\n\u001b[1;32m 1439\u001b[0m \u001b[38;5;129m@functools\u001b[39m\u001b[38;5;241m.\u001b[39mwraps(func)\n\u001b[1;32m 1440\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21minner\u001b[39m(ax, \u001b[38;5;241m*\u001b[39margs, data\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mNone\u001b[39;00m, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs):\n\u001b[1;32m 1441\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m data \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[0;32m-> 1442\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mfunc\u001b[49m\u001b[43m(\u001b[49m\u001b[43max\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;28;43mmap\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43msanitize_sequence\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43margs\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 1444\u001b[0m bound \u001b[38;5;241m=\u001b[39m new_sig\u001b[38;5;241m.\u001b[39mbind(ax, \u001b[38;5;241m*\u001b[39margs, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs)\n\u001b[1;32m 1445\u001b[0m auto_label \u001b[38;5;241m=\u001b[39m (bound\u001b[38;5;241m.\u001b[39marguments\u001b[38;5;241m.\u001b[39mget(label_namer)\n\u001b[1;32m 1446\u001b[0m \u001b[38;5;129;01mor\u001b[39;00m bound\u001b[38;5;241m.\u001b[39mkwargs\u001b[38;5;241m.\u001b[39mget(label_namer))\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/matplotlib/axes/_axes.py:6220\u001b[0m, in \u001b[0;36mAxes.pcolormesh\u001b[0;34m(self, alpha, norm, cmap, vmin, vmax, shading, antialiased, *args, **kwargs)\u001b[0m\n\u001b[1;32m 6217\u001b[0m shading \u001b[38;5;241m=\u001b[39m shading\u001b[38;5;241m.\u001b[39mlower()\n\u001b[1;32m 6218\u001b[0m kwargs\u001b[38;5;241m.\u001b[39msetdefault(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124medgecolors\u001b[39m\u001b[38;5;124m'\u001b[39m, \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mnone\u001b[39m\u001b[38;5;124m'\u001b[39m)\n\u001b[0;32m-> 6220\u001b[0m X, Y, C, shading \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_pcolorargs\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43mpcolormesh\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 6221\u001b[0m \u001b[43m \u001b[49m\u001b[43mshading\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mshading\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mkwargs\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 6222\u001b[0m coords \u001b[38;5;241m=\u001b[39m np\u001b[38;5;241m.\u001b[39mstack([X, Y], axis\u001b[38;5;241m=\u001b[39m\u001b[38;5;241m-\u001b[39m\u001b[38;5;241m1\u001b[39m)\n\u001b[1;32m 6223\u001b[0m \u001b[38;5;66;03m# convert to one dimensional array, except for 3D RGB(A) arrays\u001b[39;00m\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/matplotlib/axes/_axes.py:5786\u001b[0m, in \u001b[0;36mAxes._pcolorargs\u001b[0;34m(self, funcname, shading, *args, **kwargs)\u001b[0m\n\u001b[1;32m 5783\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m X\n\u001b[1;32m 5785\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m ncols \u001b[38;5;241m==\u001b[39m Nx:\n\u001b[0;32m-> 5786\u001b[0m X \u001b[38;5;241m=\u001b[39m \u001b[43m_interp_grid\u001b[49m\u001b[43m(\u001b[49m\u001b[43mX\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 5787\u001b[0m Y \u001b[38;5;241m=\u001b[39m _interp_grid(Y)\n\u001b[1;32m 5788\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m nrows \u001b[38;5;241m==\u001b[39m Ny:\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/matplotlib/axes/_axes.py:5768\u001b[0m, in \u001b[0;36mAxes._pcolorargs.._interp_grid\u001b[0;34m(X)\u001b[0m\n\u001b[1;32m 5766\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m np\u001b[38;5;241m.\u001b[39mshape(X)[\u001b[38;5;241m1\u001b[39m] \u001b[38;5;241m>\u001b[39m \u001b[38;5;241m1\u001b[39m:\n\u001b[1;32m 5767\u001b[0m dX \u001b[38;5;241m=\u001b[39m np\u001b[38;5;241m.\u001b[39mdiff(X, axis\u001b[38;5;241m=\u001b[39m\u001b[38;5;241m1\u001b[39m)\u001b[38;5;241m/\u001b[39m\u001b[38;5;241m2.\u001b[39m\n\u001b[0;32m-> 5768\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m (np\u001b[38;5;241m.\u001b[39mall(\u001b[43mdX\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m>\u001b[39;49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43m \u001b[49m\u001b[38;5;241;43m0\u001b[39;49m) \u001b[38;5;129;01mor\u001b[39;00m np\u001b[38;5;241m.\u001b[39mall(dX \u001b[38;5;241m<\u001b[39m\u001b[38;5;241m=\u001b[39m \u001b[38;5;241m0\u001b[39m)):\n\u001b[1;32m 5769\u001b[0m _api\u001b[38;5;241m.\u001b[39mwarn_external(\n\u001b[1;32m 5770\u001b[0m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mThe input coordinates to \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mfuncname\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m are \u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 5771\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124minterpreted as cell centers, but are not \u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 5774\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124medges, in which case, please supply \u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 5775\u001b[0m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mexplicit cell edges to \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mfuncname\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m.\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[1;32m 5776\u001b[0m X \u001b[38;5;241m=\u001b[39m np\u001b[38;5;241m.\u001b[39mhstack((X[:, [\u001b[38;5;241m0\u001b[39m]] \u001b[38;5;241m-\u001b[39m dX[:, [\u001b[38;5;241m0\u001b[39m]],\n\u001b[1;32m 5777\u001b[0m X[:, :\u001b[38;5;241m-\u001b[39m\u001b[38;5;241m1\u001b[39m] \u001b[38;5;241m+\u001b[39m dX,\n\u001b[1;32m 5778\u001b[0m X[:, [\u001b[38;5;241m-\u001b[39m\u001b[38;5;241m1\u001b[39m]] \u001b[38;5;241m+\u001b[39m dX[:, [\u001b[38;5;241m-\u001b[39m\u001b[38;5;241m1\u001b[39m]]))\n", + "\u001b[0;31mUFuncTypeError\u001b[0m: Cannot cast ufunc 'greater_equal' input 0 from dtype('\n", + "
\n", + " Figure\n", + "
\n", + " \n", + " \n", + " " + ], + "text/plain": [ + "Canvas(toolbar=Toolbar(toolitems=[('Home', 'Reset original view', 'home', 'home'), ('Back', 'Back to previous …" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'RadarArtifacts'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'Reflectivity'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/ARSCL/.ipynb_checkpoints/arsclbnd1cloth.c1-checkpoint.ipynb b/VAPs/quicklook/ARSCL/.ipynb_checkpoints/arsclbnd1cloth.c1-checkpoint.ipynb new file mode 100644 index 00000000..b365a780 --- /dev/null +++ b/VAPs/quicklook/ARSCL/.ipynb_checkpoints/arsclbnd1cloth.c1-checkpoint.ipynb @@ -0,0 +1,1937 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# ARSCLBND1CLOTH.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/arscl) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'arsclbnd1cloth'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2011-03-23', 'facility': 'C1', 'site': 'nsa', 'start_date': '1998-03-25'}, {'end_date': '2011-01-04', 'facility': 'C1', 'site': 'sgp', 'start_date': '1996-11-08'}, {'end_date': '2011-03-07', 'facility': 'C1', 'site': 'twp', 'start_date': '1999-07-01'}, {'end_date': '2009-02-14', 'facility': 'C2', 'site': 'twp', 'start_date': '1998-11-01'}, {'end_date': '2011-02-28', 'facility': 'C3', 'site': 'twp', 'start_date': '2003-01-01'}]" + ] + }, + { + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "ac6764f5", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The following locations and date ranges are available for this VAP:\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
sitefacilitystart_dateend_date
0nsaC11998-03-252011-03-23
1sgpC11996-11-082011-01-04
2twpC11999-07-012011-03-07
3twpC21998-11-012009-02-14
4twpC32003-01-012011-02-28
\n", + "
" + ], + "text/plain": [ + " site facility start_date end_date\n", + "0 nsa C1 1998-03-25 2011-03-23\n", + "1 sgp C1 1996-11-08 2011-01-04\n", + "2 twp C1 1999-07-01 2011-03-07\n", + "3 twp C2 1998-11-01 2009-02-14\n", + "4 twp C3 2003-01-01 2011-02-28" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2011-01-03'\n", + "date_end = '2011-01-04'" + ] + }, + { + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'/data/archive/sgp/sgparsclbnd1clothC1.c1'" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['20110103', '20110104']" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "51feea2e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['/data/archive/sgp/sgparsclbnd1clothC1.c1/sgparsclbnd1clothC1.c1.20110103.000000.cdf',\n", + " '/data/archive/sgp/sgparsclbnd1clothC1.c1/sgparsclbnd1clothC1.c1.20110104.000000.cdf']" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "604409ea", + "metadata": {}, + "outputs": [], + "source": [ + "# this datastream is a bit different. It has trouble merge the individual datasets." + ] + }, + { + "cell_type": "code", + "execution_count": 122, + "id": "ccbe501b", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
<xarray.Dataset>\n",
+       "Dimensions:                         (time: 17280, numlayers: 10)\n",
+       "Coordinates:\n",
+       "  * time                            (time) datetime64[ns] 2011-01-01 ... 2011...\n",
+       "Dimensions without coordinates: numlayers\n",
+       "Data variables:\n",
+       "    base_time                       datetime64[ns] 2011-01-01\n",
+       "    time_offset                     (time) timedelta64[ns] 00:00:00 ... NaT\n",
+       "    CloudBaseBestEstimate           (time) float32 -1.0 -1.0 -1.0 ... nan nan\n",
+       "    CloudLayerBottomHeightMplZwang  (time, numlayers) float32 0.0 0.0 ... nan\n",
+       "    CloudLayerTopHeightMplZwang     (time, numlayers) float32 0.0 0.0 ... nan\n",
+       "    qc_CloudLayerTopHeightMplZwang  (time, numlayers) float32 0.0 0.0 ... nan\n",
+       "Attributes:\n",
+       "    Date:                      Wed Jul 13 16:27:17 GMT 2011\n",
+       "    Version:                   $State: Exp $\n",
+       "    Number_Input_Platforms:    3\n",
+       "    Input_Platforms:           sgp30smplcmask1zwangC1.c1,sgpvceil25kC1.b1,sgp...\n",
+       "    Input_Platforms_Versions:  ?????,10.2,1.16\n",
+       "    zeb_platform:              sgparsclbnd1clothC1.c1\n",
+       "    Command_Line:              arsc1/arscl2 -s YYYYMMDD -e YYYYMMDD SITE FACI...\n",
+       "    contact:                    \n",
+       "    comment:                   If all layer top heights are 0, then the first...
" + ], + "text/plain": [ + "\n", + "Dimensions: (time: 17280, numlayers: 10)\n", + "Coordinates:\n", + " * time (time) datetime64[ns] 2011-01-01 ... 2011...\n", + "Dimensions without coordinates: numlayers\n", + "Data variables:\n", + " base_time datetime64[ns] 2011-01-01\n", + " time_offset (time) timedelta64[ns] 00:00:00 ... NaT\n", + " CloudBaseBestEstimate (time) float32 -1.0 -1.0 -1.0 ... nan nan\n", + " CloudLayerBottomHeightMplZwang (time, numlayers) float32 0.0 0.0 ... nan\n", + " CloudLayerTopHeightMplZwang (time, numlayers) float32 0.0 0.0 ... nan\n", + " qc_CloudLayerTopHeightMplZwang (time, numlayers) float32 0.0 0.0 ... nan\n", + "Attributes:\n", + " Date: Wed Jul 13 16:27:17 GMT 2011\n", + " Version: $State: Exp $\n", + " Number_Input_Platforms: 3\n", + " Input_Platforms: sgp30smplcmask1zwangC1.c1,sgpvceil25kC1.b1,sgp...\n", + " Input_Platforms_Versions: ?????,10.2,1.16\n", + " zeb_platform: sgparsclbnd1clothC1.c1\n", + " Command_Line: arsc1/arscl2 -s YYYYMMDD -e YYYYMMDD SITE FACI...\n", + " contact: \n", + " comment: If all layer top heights are 0, then the first..." + ] + }, + "execution_count": 122, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# this datastream require some special treatment to merge the files\n", + "ds_single_1 = xr.load_dataset(\"/data/archive/sgp/sgparsclbnd1clothC1.c1/sgparsclbnd1clothC1.c1.20110101.000000.cdf\")\n", + "ds_single_1\n", + "\n", + "ds_single_2 = xr.load_dataset(\"/data/archive/sgp/sgparsclbnd1clothC1.c1/sgparsclbnd1clothC1.c1.20110102.000000.cdf\")\n", + "ds_single_2\n", + "\n", + "# ds_single_1.time.data = ds_single_1.base_time.data + ds_single_1.time.data\n", + "# ds_single_2.time.data = ds_single_2.base_time.data + ds_single_2.time.data\n", + "\n", + "ds_single_1['time'] = ds_single_1.base_time.data + ds_single_1.time.data * 10000000000\n", + "ds_single_2['time'] = ds_single_2.base_time.data + ds_single_2.time.data * 10000000000\n", + "\n", + "ds_single_1['base_time'] = pd.to_datetime(ds_single_1.base_time.data)\n", + "ds_single_2['base_time'] = pd.to_datetime(ds_single_2.base_time.data)\n", + "\n", + "ds = xr.merge([ds_single_1, ds_single_2], compat='override') \n", + "ds" + ] + }, + { + "cell_type": "code", + "execution_count": 123, + "id": "dde711c1", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
<xarray.DataArray 'CloudBaseBestEstimate' (time: 8640)>\n",
+       "array([-1., -1., -1., ..., -1., -1., -1.], dtype=float32)\n",
+       "Coordinates:\n",
+       "  * time     (time) datetime64[ns] 2011-01-02 ... 2011-01-02T23:59:50\n",
+       "Attributes:\n",
+       "    long_name:  LASER Cloud Base Height Best Estimate\n",
+       "    units:      m AGL\n",
+       "    comment:    -3. Data do not exist, -2. Data exist but no retrieval, -1. C...
" + ], + "text/plain": [ + "\n", + "array([-1., -1., -1., ..., -1., -1., -1.], dtype=float32)\n", + "Coordinates:\n", + " * time (time) datetime64[ns] 2011-01-02 ... 2011-01-02T23:59:50\n", + "Attributes:\n", + " long_name: LASER Cloud Base Height Best Estimate\n", + " units: m AGL\n", + " comment: -3. Data do not exist, -2. Data exist but no retrieval, -1. C..." + ] + }, + "execution_count": 123, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ds_single_2.CloudBaseBestEstimate" + ] + }, + { + "cell_type": "code", + "execution_count": 72, + "id": "7c1839ad", + "metadata": {}, + "outputs": [ + { + "ename": "MergeError", + "evalue": "conflicting values for variable 'base_time' on objects to be combined. You can skip this check by specifying compat='override'.", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/act/io/armfiles.py:143\u001b[0m, in \u001b[0;36mread_netcdf\u001b[0;34m(filenames, concat_dim, return_None, combine, decode_times, use_cftime, use_base_time, combine_attrs, cleanup_qc, keep_variables, **kwargs)\u001b[0m\n\u001b[1;32m 141\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[1;32m 142\u001b[0m \u001b[38;5;66;03m# Read data file with Xarray function\u001b[39;00m\n\u001b[0;32m--> 143\u001b[0m ds \u001b[38;5;241m=\u001b[39m \u001b[43mxr\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mopen_mfdataset\u001b[49m\u001b[43m(\u001b[49m\u001b[43mfilenames\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 145\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m except_tuple \u001b[38;5;28;01mas\u001b[39;00m exception:\n\u001b[1;32m 146\u001b[0m \u001b[38;5;66;03m# If requested return None for File not found error\u001b[39;00m\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/backends/api.py:1026\u001b[0m, in \u001b[0;36mopen_mfdataset\u001b[0;34m(paths, chunks, concat_dim, compat, preprocess, engine, data_vars, coords, combine, parallel, join, attrs_file, combine_attrs, **kwargs)\u001b[0m\n\u001b[1;32m 1023\u001b[0m \u001b[38;5;28;01melif\u001b[39;00m combine \u001b[38;5;241m==\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mby_coords\u001b[39m\u001b[38;5;124m\"\u001b[39m:\n\u001b[1;32m 1024\u001b[0m \u001b[38;5;66;03m# Redo ordering from coordinates, ignoring how they were ordered\u001b[39;00m\n\u001b[1;32m 1025\u001b[0m \u001b[38;5;66;03m# previously\u001b[39;00m\n\u001b[0;32m-> 1026\u001b[0m combined \u001b[38;5;241m=\u001b[39m \u001b[43mcombine_by_coords\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 1027\u001b[0m \u001b[43m \u001b[49m\u001b[43mdatasets\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1028\u001b[0m \u001b[43m \u001b[49m\u001b[43mcompat\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcompat\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1029\u001b[0m \u001b[43m \u001b[49m\u001b[43mdata_vars\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mdata_vars\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1030\u001b[0m \u001b[43m \u001b[49m\u001b[43mcoords\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcoords\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1031\u001b[0m \u001b[43m \u001b[49m\u001b[43mjoin\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mjoin\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1032\u001b[0m \u001b[43m \u001b[49m\u001b[43mcombine_attrs\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcombine_attrs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1033\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 1034\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/core/combine.py:982\u001b[0m, in \u001b[0;36mcombine_by_coords\u001b[0;34m(data_objects, compat, data_vars, coords, fill_value, join, combine_attrs, datasets)\u001b[0m\n\u001b[1;32m 981\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m \u001b[38;5;28mvars\u001b[39m, datasets_with_same_vars \u001b[38;5;129;01min\u001b[39;00m grouped_by_vars:\n\u001b[0;32m--> 982\u001b[0m concatenated \u001b[38;5;241m=\u001b[39m \u001b[43m_combine_single_variable_hypercube\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 983\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43mlist\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mdatasets_with_same_vars\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 984\u001b[0m \u001b[43m \u001b[49m\u001b[43mfill_value\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mfill_value\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 985\u001b[0m \u001b[43m \u001b[49m\u001b[43mdata_vars\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mdata_vars\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 986\u001b[0m \u001b[43m \u001b[49m\u001b[43mcoords\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcoords\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 987\u001b[0m \u001b[43m \u001b[49m\u001b[43mcompat\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcompat\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 988\u001b[0m \u001b[43m \u001b[49m\u001b[43mjoin\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mjoin\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 989\u001b[0m \u001b[43m \u001b[49m\u001b[43mcombine_attrs\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcombine_attrs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 990\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 991\u001b[0m concatenated_grouped_by_data_vars\u001b[38;5;241m.\u001b[39mappend(concatenated)\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/core/combine.py:629\u001b[0m, in \u001b[0;36m_combine_single_variable_hypercube\u001b[0;34m(datasets, fill_value, data_vars, coords, compat, join, combine_attrs)\u001b[0m\n\u001b[1;32m 624\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\n\u001b[1;32m 625\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mAt least one Dataset is required to resolve variable names \u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 626\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mfor combined hypercube.\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 627\u001b[0m )\n\u001b[0;32m--> 629\u001b[0m combined_ids, concat_dims \u001b[38;5;241m=\u001b[39m \u001b[43m_infer_concat_order_from_coords\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mlist\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mdatasets\u001b[49m\u001b[43m)\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 631\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m fill_value \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[1;32m 632\u001b[0m \u001b[38;5;66;03m# check that datasets form complete hypercube\u001b[39;00m\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/core/combine.py:149\u001b[0m, in \u001b[0;36m_infer_concat_order_from_coords\u001b[0;34m(datasets)\u001b[0m\n\u001b[1;32m 148\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mlen\u001b[39m(datasets) \u001b[38;5;241m>\u001b[39m \u001b[38;5;241m1\u001b[39m \u001b[38;5;129;01mand\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m concat_dims:\n\u001b[0;32m--> 149\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\n\u001b[1;32m 150\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mCould not find any dimension coordinates to use to \u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 151\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124morder the datasets for concatenation\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 152\u001b[0m )\n\u001b[1;32m 154\u001b[0m combined_ids \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mdict\u001b[39m(\u001b[38;5;28mzip\u001b[39m(tile_ids, datasets))\n", + "\u001b[0;31mValueError\u001b[0m: Could not find any dimension coordinates to use to order the datasets for concatenation", + "\nDuring handling of the above exception, another exception occurred:\n", + "\u001b[0;31mMergeError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[0;32mIn[72], line 5\u001b[0m\n\u001b[1;32m 1\u001b[0m files_filter \u001b[38;5;241m=\u001b[39m \\\n\u001b[1;32m 2\u001b[0m [\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m/data/archive/sgp/sgparsclbnd1clothC1.c1/sgparsclbnd1clothC1.c1.20110104.000000.cdf\u001b[39m\u001b[38;5;124m'\u001b[39m,\n\u001b[1;32m 3\u001b[0m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124m/data/archive/sgp/sgparsclbnd1clothC1.c1/sgparsclbnd1clothC1.c1.20110106.000000.cdf\u001b[39m\u001b[38;5;124m'\u001b[39m]\n\u001b[0;32m----> 5\u001b[0m ds_multi \u001b[38;5;241m=\u001b[39m \u001b[43mact\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mio\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43marmfiles\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mread_netcdf\u001b[49m\u001b[43m(\u001b[49m\u001b[43mfiles_list\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 6\u001b[0m ds_multi\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/act/io/armfiles.py:164\u001b[0m, in \u001b[0;36mread_netcdf\u001b[0;34m(filenames, concat_dim, return_None, combine, decode_times, use_cftime, use_base_time, combine_attrs, cleanup_qc, keep_variables, **kwargs)\u001b[0m\n\u001b[1;32m 157\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m (\n\u001b[1;32m 158\u001b[0m kwargs[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mcombine\u001b[39m\u001b[38;5;124m'\u001b[39m] \u001b[38;5;241m!=\u001b[39m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mnested\u001b[39m\u001b[38;5;124m'\u001b[39m\n\u001b[1;32m 159\u001b[0m \u001b[38;5;129;01mand\u001b[39;00m \u001b[38;5;28mtype\u001b[39m(exception)\u001b[38;5;241m.\u001b[39m\u001b[38;5;18m__name__\u001b[39m \u001b[38;5;241m==\u001b[39m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mValueError\u001b[39m\u001b[38;5;124m'\u001b[39m\n\u001b[1;32m 160\u001b[0m \u001b[38;5;129;01mand\u001b[39;00m exception\u001b[38;5;241m.\u001b[39margs[\u001b[38;5;241m0\u001b[39m] \u001b[38;5;241m==\u001b[39m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mCould not find any dimension coordinates \u001b[39m\u001b[38;5;124m'\u001b[39m\n\u001b[1;32m 161\u001b[0m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mto use to order the datasets for concatenation\u001b[39m\u001b[38;5;124m'\u001b[39m\n\u001b[1;32m 162\u001b[0m ):\n\u001b[1;32m 163\u001b[0m kwargs[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mcombine\u001b[39m\u001b[38;5;124m'\u001b[39m] \u001b[38;5;241m=\u001b[39m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mnested\u001b[39m\u001b[38;5;124m'\u001b[39m\n\u001b[0;32m--> 164\u001b[0m ds \u001b[38;5;241m=\u001b[39m \u001b[43mxr\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mopen_mfdataset\u001b[49m\u001b[43m(\u001b[49m\u001b[43mfilenames\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 166\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m 167\u001b[0m \u001b[38;5;66;03m# When all else fails raise the orginal exception\u001b[39;00m\n\u001b[1;32m 168\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m exception\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/backends/api.py:1013\u001b[0m, in \u001b[0;36mopen_mfdataset\u001b[0;34m(paths, chunks, concat_dim, compat, preprocess, engine, data_vars, coords, combine, parallel, join, attrs_file, combine_attrs, **kwargs)\u001b[0m\n\u001b[1;32m 1009\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[1;32m 1010\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m combine \u001b[38;5;241m==\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mnested\u001b[39m\u001b[38;5;124m\"\u001b[39m:\n\u001b[1;32m 1011\u001b[0m \u001b[38;5;66;03m# Combined nested list by successive concat and merge operations\u001b[39;00m\n\u001b[1;32m 1012\u001b[0m \u001b[38;5;66;03m# along each dimension, using structure given by \"ids\"\u001b[39;00m\n\u001b[0;32m-> 1013\u001b[0m combined \u001b[38;5;241m=\u001b[39m \u001b[43m_nested_combine\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 1014\u001b[0m \u001b[43m \u001b[49m\u001b[43mdatasets\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1015\u001b[0m \u001b[43m \u001b[49m\u001b[43mconcat_dims\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mconcat_dim\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1016\u001b[0m \u001b[43m \u001b[49m\u001b[43mcompat\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcompat\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1017\u001b[0m \u001b[43m \u001b[49m\u001b[43mdata_vars\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mdata_vars\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1018\u001b[0m \u001b[43m \u001b[49m\u001b[43mcoords\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcoords\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1019\u001b[0m \u001b[43m \u001b[49m\u001b[43mids\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mids\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1020\u001b[0m \u001b[43m \u001b[49m\u001b[43mjoin\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mjoin\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1021\u001b[0m \u001b[43m \u001b[49m\u001b[43mcombine_attrs\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcombine_attrs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1022\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 1023\u001b[0m \u001b[38;5;28;01melif\u001b[39;00m combine \u001b[38;5;241m==\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mby_coords\u001b[39m\u001b[38;5;124m\"\u001b[39m:\n\u001b[1;32m 1024\u001b[0m \u001b[38;5;66;03m# Redo ordering from coordinates, ignoring how they were ordered\u001b[39;00m\n\u001b[1;32m 1025\u001b[0m \u001b[38;5;66;03m# previously\u001b[39;00m\n\u001b[1;32m 1026\u001b[0m combined \u001b[38;5;241m=\u001b[39m combine_by_coords(\n\u001b[1;32m 1027\u001b[0m datasets,\n\u001b[1;32m 1028\u001b[0m compat\u001b[38;5;241m=\u001b[39mcompat,\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 1032\u001b[0m combine_attrs\u001b[38;5;241m=\u001b[39mcombine_attrs,\n\u001b[1;32m 1033\u001b[0m )\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/core/combine.py:365\u001b[0m, in \u001b[0;36m_nested_combine\u001b[0;34m(datasets, concat_dims, compat, data_vars, coords, ids, fill_value, join, combine_attrs)\u001b[0m\n\u001b[1;32m 362\u001b[0m _check_shape_tile_ids(combined_ids)\n\u001b[1;32m 364\u001b[0m \u001b[38;5;66;03m# Apply series of concatenate or merge operations along each dimension\u001b[39;00m\n\u001b[0;32m--> 365\u001b[0m combined \u001b[38;5;241m=\u001b[39m \u001b[43m_combine_nd\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 366\u001b[0m \u001b[43m \u001b[49m\u001b[43mcombined_ids\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 367\u001b[0m \u001b[43m \u001b[49m\u001b[43mconcat_dims\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 368\u001b[0m \u001b[43m \u001b[49m\u001b[43mcompat\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcompat\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 369\u001b[0m \u001b[43m \u001b[49m\u001b[43mdata_vars\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mdata_vars\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 370\u001b[0m \u001b[43m \u001b[49m\u001b[43mcoords\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcoords\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 371\u001b[0m \u001b[43m \u001b[49m\u001b[43mfill_value\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mfill_value\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 372\u001b[0m \u001b[43m \u001b[49m\u001b[43mjoin\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mjoin\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 373\u001b[0m \u001b[43m \u001b[49m\u001b[43mcombine_attrs\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcombine_attrs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 374\u001b[0m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 375\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m combined\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/core/combine.py:239\u001b[0m, in \u001b[0;36m_combine_nd\u001b[0;34m(combined_ids, concat_dims, data_vars, coords, compat, fill_value, join, combine_attrs)\u001b[0m\n\u001b[1;32m 235\u001b[0m \u001b[38;5;66;03m# Each iteration of this loop reduces the length of the tile_ids tuples\u001b[39;00m\n\u001b[1;32m 236\u001b[0m \u001b[38;5;66;03m# by one. It always combines along the first dimension, removing the first\u001b[39;00m\n\u001b[1;32m 237\u001b[0m \u001b[38;5;66;03m# element of the tuple\u001b[39;00m\n\u001b[1;32m 238\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m concat_dim \u001b[38;5;129;01min\u001b[39;00m concat_dims:\n\u001b[0;32m--> 239\u001b[0m combined_ids \u001b[38;5;241m=\u001b[39m \u001b[43m_combine_all_along_first_dim\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 240\u001b[0m \u001b[43m \u001b[49m\u001b[43mcombined_ids\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 241\u001b[0m \u001b[43m \u001b[49m\u001b[43mdim\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mconcat_dim\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 242\u001b[0m \u001b[43m \u001b[49m\u001b[43mdata_vars\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mdata_vars\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 243\u001b[0m \u001b[43m \u001b[49m\u001b[43mcoords\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcoords\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 244\u001b[0m \u001b[43m \u001b[49m\u001b[43mcompat\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcompat\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 245\u001b[0m \u001b[43m \u001b[49m\u001b[43mfill_value\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mfill_value\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 246\u001b[0m \u001b[43m \u001b[49m\u001b[43mjoin\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mjoin\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 247\u001b[0m \u001b[43m \u001b[49m\u001b[43mcombine_attrs\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcombine_attrs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 248\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 249\u001b[0m (combined_ds,) \u001b[38;5;241m=\u001b[39m combined_ids\u001b[38;5;241m.\u001b[39mvalues()\n\u001b[1;32m 250\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m combined_ds\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/core/combine.py:275\u001b[0m, in \u001b[0;36m_combine_all_along_first_dim\u001b[0;34m(combined_ids, dim, data_vars, coords, compat, fill_value, join, combine_attrs)\u001b[0m\n\u001b[1;32m 273\u001b[0m combined_ids \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mdict\u001b[39m(\u001b[38;5;28msorted\u001b[39m(group))\n\u001b[1;32m 274\u001b[0m datasets \u001b[38;5;241m=\u001b[39m combined_ids\u001b[38;5;241m.\u001b[39mvalues()\n\u001b[0;32m--> 275\u001b[0m new_combined_ids[new_id] \u001b[38;5;241m=\u001b[39m \u001b[43m_combine_1d\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 276\u001b[0m \u001b[43m \u001b[49m\u001b[43mdatasets\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mdim\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mcompat\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mdata_vars\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mcoords\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mfill_value\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mjoin\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mcombine_attrs\u001b[49m\n\u001b[1;32m 277\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 278\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m new_combined_ids\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/core/combine.py:320\u001b[0m, in \u001b[0;36m_combine_1d\u001b[0;34m(datasets, concat_dim, compat, data_vars, coords, fill_value, join, combine_attrs)\u001b[0m\n\u001b[1;32m 318\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m\n\u001b[1;32m 319\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[0;32m--> 320\u001b[0m combined \u001b[38;5;241m=\u001b[39m \u001b[43mmerge\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 321\u001b[0m \u001b[43m \u001b[49m\u001b[43mdatasets\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 322\u001b[0m \u001b[43m \u001b[49m\u001b[43mcompat\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcompat\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 323\u001b[0m \u001b[43m \u001b[49m\u001b[43mfill_value\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mfill_value\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 324\u001b[0m \u001b[43m \u001b[49m\u001b[43mjoin\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mjoin\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 325\u001b[0m \u001b[43m \u001b[49m\u001b[43mcombine_attrs\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcombine_attrs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 326\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 328\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m combined\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/core/merge.py:1025\u001b[0m, in \u001b[0;36mmerge\u001b[0;34m(objects, compat, join, fill_value, combine_attrs)\u001b[0m\n\u001b[1;32m 1022\u001b[0m obj \u001b[38;5;241m=\u001b[39m obj\u001b[38;5;241m.\u001b[39mto_dataset(promote_attrs\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mTrue\u001b[39;00m) \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(obj, DataArray) \u001b[38;5;28;01melse\u001b[39;00m obj\n\u001b[1;32m 1023\u001b[0m dict_like_objects\u001b[38;5;241m.\u001b[39mappend(obj)\n\u001b[0;32m-> 1025\u001b[0m merge_result \u001b[38;5;241m=\u001b[39m \u001b[43mmerge_core\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 1026\u001b[0m \u001b[43m \u001b[49m\u001b[43mdict_like_objects\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1027\u001b[0m \u001b[43m \u001b[49m\u001b[43mcompat\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1028\u001b[0m \u001b[43m \u001b[49m\u001b[43mjoin\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1029\u001b[0m \u001b[43m \u001b[49m\u001b[43mcombine_attrs\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcombine_attrs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1030\u001b[0m \u001b[43m \u001b[49m\u001b[43mfill_value\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mfill_value\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1031\u001b[0m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 1032\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m Dataset\u001b[38;5;241m.\u001b[39m_construct_direct(\u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mmerge_result\u001b[38;5;241m.\u001b[39m_asdict())\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/core/merge.py:757\u001b[0m, in \u001b[0;36mmerge_core\u001b[0;34m(objects, compat, join, combine_attrs, priority_arg, explicit_coords, indexes, fill_value)\u001b[0m\n\u001b[1;32m 755\u001b[0m collected \u001b[38;5;241m=\u001b[39m collect_variables_and_indexes(aligned, indexes\u001b[38;5;241m=\u001b[39mindexes)\n\u001b[1;32m 756\u001b[0m prioritized \u001b[38;5;241m=\u001b[39m _get_priority_vars_and_indexes(aligned, priority_arg, compat\u001b[38;5;241m=\u001b[39mcompat)\n\u001b[0;32m--> 757\u001b[0m variables, out_indexes \u001b[38;5;241m=\u001b[39m \u001b[43mmerge_collected\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 758\u001b[0m \u001b[43m \u001b[49m\u001b[43mcollected\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mprioritized\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mcompat\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcompat\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mcombine_attrs\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcombine_attrs\u001b[49m\n\u001b[1;32m 759\u001b[0m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 761\u001b[0m dims \u001b[38;5;241m=\u001b[39m calculate_dimensions(variables)\n\u001b[1;32m 763\u001b[0m coord_names, noncoord_names \u001b[38;5;241m=\u001b[39m determine_coords(coerced)\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/core/merge.py:302\u001b[0m, in \u001b[0;36mmerge_collected\u001b[0;34m(grouped, prioritized, compat, combine_attrs, equals)\u001b[0m\n\u001b[1;32m 300\u001b[0m variables \u001b[38;5;241m=\u001b[39m [variable \u001b[38;5;28;01mfor\u001b[39;00m variable, _ \u001b[38;5;129;01min\u001b[39;00m elements_list]\n\u001b[1;32m 301\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m--> 302\u001b[0m merged_vars[name] \u001b[38;5;241m=\u001b[39m \u001b[43munique_variable\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 303\u001b[0m \u001b[43m \u001b[49m\u001b[43mname\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mvariables\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mcompat\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mequals\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mget\u001b[49m\u001b[43m(\u001b[49m\u001b[43mname\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mNone\u001b[39;49;00m\u001b[43m)\u001b[49m\n\u001b[1;32m 304\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 305\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m MergeError:\n\u001b[1;32m 306\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m compat \u001b[38;5;241m!=\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mminimal\u001b[39m\u001b[38;5;124m\"\u001b[39m:\n\u001b[1;32m 307\u001b[0m \u001b[38;5;66;03m# we need more than \"minimal\" compatibility (for which\u001b[39;00m\n\u001b[1;32m 308\u001b[0m \u001b[38;5;66;03m# we drop conflicting coordinates)\u001b[39;00m\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/core/merge.py:156\u001b[0m, in \u001b[0;36munique_variable\u001b[0;34m(name, variables, compat, equals)\u001b[0m\n\u001b[1;32m 153\u001b[0m \u001b[38;5;28;01mbreak\u001b[39;00m\n\u001b[1;32m 155\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m equals:\n\u001b[0;32m--> 156\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m MergeError(\n\u001b[1;32m 157\u001b[0m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mconflicting values for variable \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mname\u001b[38;5;132;01m!r}\u001b[39;00m\u001b[38;5;124m on objects to be combined. \u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 158\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mYou can skip this check by specifying compat=\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124moverride\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m.\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 159\u001b[0m )\n\u001b[1;32m 161\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m combine_method:\n\u001b[1;32m 162\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m var \u001b[38;5;129;01min\u001b[39;00m variables[\u001b[38;5;241m1\u001b[39m:]:\n", + "\u001b[0;31mMergeError\u001b[0m: conflicting values for variable 'base_time' on objects to be combined. You can skip this check by specifying compat='override'." + ] + } + ], + "source": [ + "files_filter = \\\n", + "['/data/archive/sgp/sgparsclbnd1clothC1.c1/sgparsclbnd1clothC1.c1.20110104.000000.cdf',\n", + " '/data/archive/sgp/sgparsclbnd1clothC1.c1/sgparsclbnd1clothC1.c1.20110106.000000.cdf']\n", + "\n", + "ds_multi = act.io.armfiles.read_netcdf(files_list)\n", + "ds_multi" + ] + }, + { + "cell_type": "code", + "execution_count": 101, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2 files loaded\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
<xarray.Dataset>\n",
+       "Dimensions:                         (time: 17280, numlayers: 10)\n",
+       "Coordinates:\n",
+       "  * time                            (time) datetime64[ns] 2011-01-03 ... 2011...\n",
+       "Dimensions without coordinates: numlayers\n",
+       "Data variables:\n",
+       "    base_time                       datetime64[ns] 2011-01-03\n",
+       "    time_offset                     (time) timedelta64[ns] 00:00:00 ... NaT\n",
+       "    CloudBaseBestEstimate           (time) float32 -1.0 -1.0 -1.0 ... nan nan\n",
+       "    CloudLayerBottomHeightMplZwang  (time, numlayers) float32 0.0 0.0 ... nan\n",
+       "    CloudLayerTopHeightMplZwang     (time, numlayers) float32 0.0 0.0 ... nan\n",
+       "    qc_CloudLayerTopHeightMplZwang  (time, numlayers) float32 0.0 0.0 ... nan\n",
+       "Attributes:\n",
+       "    Date:                      Wed Jul 13 16:28:57 GMT 2011\n",
+       "    Version:                   $State: Exp $\n",
+       "    Number_Input_Platforms:    3\n",
+       "    Input_Platforms:           sgp30smplcmask1zwangC1.c1,sgpvceil25kC1.b1,sgp...\n",
+       "    Input_Platforms_Versions:  ?????,10.2,1.16\n",
+       "    zeb_platform:              sgparsclbnd1clothC1.c1\n",
+       "    Command_Line:              arsc1/arscl2 -s YYYYMMDD -e YYYYMMDD SITE FACI...\n",
+       "    contact:                    \n",
+       "    comment:                   If all layer top heights are 0, then the first...
" + ], + "text/plain": [ + "\n", + "Dimensions: (time: 17280, numlayers: 10)\n", + "Coordinates:\n", + " * time (time) datetime64[ns] 2011-01-03 ... 2011...\n", + "Dimensions without coordinates: numlayers\n", + "Data variables:\n", + " base_time datetime64[ns] 2011-01-03\n", + " time_offset (time) timedelta64[ns] 00:00:00 ... NaT\n", + " CloudBaseBestEstimate (time) float32 -1.0 -1.0 -1.0 ... nan nan\n", + " CloudLayerBottomHeightMplZwang (time, numlayers) float32 0.0 0.0 ... nan\n", + " CloudLayerTopHeightMplZwang (time, numlayers) float32 0.0 0.0 ... nan\n", + " qc_CloudLayerTopHeightMplZwang (time, numlayers) float32 0.0 0.0 ... nan\n", + "Attributes:\n", + " Date: Wed Jul 13 16:28:57 GMT 2011\n", + " Version: $State: Exp $\n", + " Number_Input_Platforms: 3\n", + " Input_Platforms: sgp30smplcmask1zwangC1.c1,sgpvceil25kC1.b1,sgp...\n", + " Input_Platforms_Versions: ?????,10.2,1.16\n", + " zeb_platform: sgparsclbnd1clothC1.c1\n", + " Command_Line: arsc1/arscl2 -s YYYYMMDD -e YYYYMMDD SITE FACI...\n", + " contact: \n", + " comment: If all layer top heights are 0, then the first..." + ] + }, + "execution_count": 101, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = ds_single_2\n", + "ds = data\n", + "# ds = act.io.armfiles.read_netcdf(files_list, compat='override')\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": 102, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['CloudBaseBestEstimate']" + ] + }, + { + "cell_type": "code", + "execution_count": 115, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/kefeimo/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/act/plotting/plot.py:81: UserWarning: Could not discern datastreamname and dict or tuple were not provided. Using defaultname of act_datastream!\n", + " warnings.warn(\n" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "14e3ab08f694414e84a6909356fbe15a", + "version_major": 2, + "version_minor": 0 + }, + "image/png": "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izZt2mDatGnw9PTEnj17AABr166Fm5sbvv76a3Tq1Ant27fHt99+i6ysLGzfvl308p8/fx6CICAqKkr0fRMREdkL37ElIiKyQMeOHeHmVv1cOCQkBNHR0erP7u7uCAoKQk5ODgDg8OHDOH/+PPz8/LTyKSkpwYULF8QptAZBEABAPdgUERFRXcDAloiIyAJyuVzrs0wm07ussrISAFBZWYnY2FisWbNGJy/VO7DmatCgAfLz83WW5+XloUGDBmbl0aZNG8hkMmRkZDh0KiEiIiJbYldkIiIiO4qJicG5c+cQHByM1q1ba/3z9/e3KK+oqCgcOnRIZ/nBgwfRrl07s/IIDAzEkCFDsHjxYhQWFuqs53y1REQkRQxsiYiI7OjJJ59Eo0aN8PDDD2PXrl24dOkSduzYgddeew1XrlyxKK9JkybhwoULeOmll/DXX3/h7NmzWLx4MVasWIE333zT7HyWLFmCiooKdO/eHevWrcO5c+eQkZGB//znP4iLi7P0EImIiByOgS0REZEd1atXDzt37kRERARGjx6N9u3b47nnnkNxcbHZ3YdVWrRogV27duHChQsYPHgw7r//fqxcuRIrV67EY489ZnY+kZGROHLkCPr164epU6ciOjoagwYNQmpqKpYuXWrpIRIRETmcTFCNIkFEREROYfv27ejXrx9yc3MdPv2OM5WFiIjIELbYEhEROalmzZrhiSeecNj+O3bsiIceeshh+yciIjIXW2yJiIicTHFxMa5evQoAqF+/PkJDQx1SjsuXL0OpVAIAWrZsqTXNERERkTNhYEtERERERESS9v9ClzziBzGLBAAAAABJRU5ErkJggg==", + "text/html": [ + "\n", + "
\n", + "
\n", + " Figure\n", + "
\n", + " \n", + "
\n", + " " + ], + "text/plain": [ + "Canvas(toolbar=Toolbar(toolitems=[('Home', 'Reset original view', 'home', 'home'), ('Back', 'Back to previous …" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'CloudLayerTopHeightMplCamp'" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "QC not available for the selected field: CloudLayerTopHeightMplCamp\n" + ] + } + ], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": 116, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/kefeimo/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/act/plotting/plot.py:81: UserWarning: Could not discern datastreamname and dict or tuple were not provided. Using defaultname of act_datastream!\n", + " warnings.warn(\n" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "29438b2a84d24cb3b3d1d4be9f4dae0c", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "AppLayout(children=(Dropdown(description='Field:', layout=Layout(grid_area='header', margin='0px 30% 0px 20%',…" + ] + }, + "execution_count": 116, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'CloudBaseBestEstimate'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1d7932f0", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/ARSCL/ARSCL_tutorial.ipynb b/VAPs/quicklook/ARSCL/ARSCL_tutorial.ipynb new file mode 100644 index 00000000..0e130cc5 --- /dev/null +++ b/VAPs/quicklook/ARSCL/ARSCL_tutorial.ipynb @@ -0,0 +1,838 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# ARSCL1CLOTH.C1 Notebook\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/arscl) for more information about this vap." + ] + }, + { + "cell_type": "markdown", + "id": "97097763", + "metadata": {}, + "source": [ + "In this notebook, we demonstrate the workflow to explore ARM vap data (using arscl1cloth as an example.) Value-added products (VAPs) are higher-order data products that have been analyzed and processed to ease scientists’ use of ARM data in atmospheric research and global climate models. \n", + "Here is the main content we will cover." + ] + }, + { + "cell_type": "markdown", + "id": "eddec40f", + "metadata": {}, + "source": [ + "# Table of Content\n", + "## Access the data\n", + "* How to retrieve the data\n", + "* Data path and file name conventions\n", + "* Load data\n", + "## Explore the data\n", + "* NetCDF Data structure\n", + "* Xarray essentials\n", + "* Xarray Variable\n", + "## Plot the data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "import random\n", + "\n", + "import glob\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "2372d5be", + "metadata": {}, + "source": [ + "## Access the data" + ] + }, + { + "cell_type": "markdown", + "id": "5b0d9684", + "metadata": {}, + "source": [ + "### How to retrieve the data\n", + "We assume the path \"/data/archive\" is available where you are running this notebook. You can use `os.path.exists(\"/data/archive\")` to verify if the path exists at your machine.\n", + "\n", + "\n", + "### Data path and file name conventions\n", + "There several common terminologies regarding ARM data, for example, data-stream-name, data-level, site, facility, instrument, etc. (For more details, please see [ARM Data File Standards Version 1.2](https://www.google.com/search?q=arm+datastream+facility+cite+definition&rlz=1C1GCEJ_enUS1029US1029&ei=hb41ZICrBPukqtsPvMODIA&ved=0ahUKEwjAgeiS0aL-AhV7kmoFHbzhAAQQ4dUDCBA&uact=5&oq=arm+datastream+facility+cite+definition&gs_lcp=Cgxnd3Mtd2l6LXNlcnAQAzIFCAAQogQyBQgAEKIEMgUIABCiBDIFCAAQogQyBQgAEKIEOgoIABBHENYEELADOgoIIRCgARDDBBAKSgQIQRgAUJ4DWIoOYO0PaAFwAXgAgAGMAYgBuQmSAQMyLjmYAQCgAQHIAQjAAQE&sclient=gws-wiz-serp).) \n", + "\n", + "For example, this notebook is called `arscl1cloth.c1`, where `arscl1cloth` is the \"datastream name\", and the `{process.ds_class_level}}` is the \"data level\".\n", + "\n", + "This datastream also contains site `nsa` and facility `C1`. (Note: individual datastream might have multiple site-facility pairs.)\n", + "In such a case, the data of this data-stream is stored at `/data/archive/nsa/nsaarscl1clothC1.c1`, which is in the format of `//.`. We can use the following method to assign the data-stream directory `datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )`\n", + "\n", + "The data files under datastream_dir also follows naming conventions. But once reach the datastream_dir level, the most import file naming convention to differentiate the files is \"yyyyMMdd.hhmmss\", which comes handy to filter out files based on datetime. For example, we can use `glob.glob(f'{datastream_dir}/*.200709*.*')` to filter files in 2007 September.\n", + "\n", + "(Note: refer to the https://adc.arm.gov/discovery/#/ and https://adc.arm.gov/solr8/metadata/select API to explore ARM datastream and assoicated available sites and facilities\n", + "\n", + "Please see the following examples in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d7e9eb85", + "metadata": {}, + "outputs": [], + "source": [ + "# Verify if DATA_DIR path exists\n", + "DATA_DIR = \"/data/archive\"\n", + "os.path.exists(DATA_DIR)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "586993fd", + "metadata": {}, + "outputs": [], + "source": [ + "# Speicify datastream_dir following the path conventions and check its existence\n", + "DATASTREAM_NAME = \"arscl1cloth\"\n", + "DATA_LEVEL = \"c1\"\n", + "site = \"nsa\"\n", + "facility = \"C1\"\n", + "datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )\n", + "print(datastream_dir)\n", + "print(os.path.exists(datastream_dir))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0742f7c1", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: list 5 (random) files under datastream_dir\n", + "files = os.listdir(datastream_dir)\n", + "files[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39b98a36", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: get most recent file\n", + "list_of_files = glob.glob(f\"{datastream_dir}/*\") # * means all if need specific format then *.csv\n", + "latest_file = max(list_of_files, key=os.path.getctime)\n", + "latest_file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "902d514e", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus sort datastream files based on datetime\n", + "files = os.listdir(datastream_dir)\n", + "file_sorted = files.copy()\n", + "file_sorted.sort() \n", + "print(file_sorted[:5])\n", + "\n", + "# to reverse\n", + "file_sorted_reverse = files.copy() \n", + "file_sorted_reverse.sort(reverse=True)\n", + "print(file_sorted_reverse[:5])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec5923b2", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: pattern matching\n", + "# filter the 200709** files under datastream_dir\n", + "files_filter = glob.glob(f'{datastream_dir}/*.200709*.*')\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c4a4aa18", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: use armnotebook_utils.file_filter (TODO) to filter files based on datastream info \n" + ] + }, + { + "cell_type": "markdown", + "id": "cfeb9efc", + "metadata": {}, + "source": [ + "### Load data\n", + "The arm data is stored in [NetCDF](https://en.wikipedia.org/wiki/NetCDF#:~:text=%22NetCDF%20(network%20Common%20Data%20Form,format%20for%20representing%20scientific%20data.) format. We can use xarray's `open_dataset` method to load single file, or `open_mfdataset` to open multiple files. (Note: the latter will still return a single xarray dataset object by combining multiple files.)\n", + "\n", + "Note: open_dataset keeps the file handle open and lazy loads its contents. All parameters are passed directly to open_dataset. It is a preferable method over load_dataset for memory-effiency. (more details, please see [xarray.open_dataset](https://docs.xarray.dev/en/stable/generated/xarray.open_dataset.html))\n", + "\n", + "See the following example in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a440a329", + "metadata": {}, + "outputs": [], + "source": [ + "# open single file\n", + "full_path = latest_file\n", + "print(full_path)\n", + "ds_single = xr.open_dataset(full_path)\n", + "ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0143a3d", + "metadata": {}, + "outputs": [], + "source": [ + "print(ds_single)\n", + "print(type(ds_single))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c0f8939", + "metadata": {}, + "outputs": [], + "source": [ + "# open multiple files\n", + "n_files = 3\n", + "full_paths = [os.path.join(datastream_dir, f_path) for f_path in file_sorted_reverse[:n_files]]\n", + "print(full_paths)\n", + "try: # Note: sometimes multiple files cannot be merged, so we used try except here.\n", + " ds_mutiple = xr.open_mfdataset(full_paths)\n", + " # ds_mutiple\n", + " print(type(ds_mutiple))\n", + " print(ds_mutiple)\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "3fa59943", + "metadata": {}, + "source": [ + "## Explore the data" + ] + }, + { + "cell_type": "markdown", + "id": "571f69b9", + "metadata": {}, + "source": [ + "### NetCDF Data structure \n", + "\n", + "\n", + "(If you are confident with NetCDF basics and xarray essentials, feel free to skip this session.)\n", + "Before we dive into data exploration, there are some eseential concpets we should be familiar with (shown below): \n", + "* Dataset\n", + "* Data array\n", + "* Variable\n", + "* Dimenssion\n", + "* Coordinate\n", + "* Data Type\n", + "* Meta Data (Attributes)\n", + "\n", + "We will not go into details about NetCDF basics and here are some references you might find helpful\n", + "* [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#)\n", + "* [Network Common Data Form (NetCDF)](https://www.unidata.ucar.edu/software/netcdf/) and [A Brief History of (netCDF) Time](https://www.unidata.ucar.edu/software/netcdf/time/recs.html)\n", + "\n", + "\n", + "\n", + "### Xarray essentials\n", + "Earlier we introduced the xarray pacakge and used `open_dataset` and `open_mfdataset` to retrieve NetCDF data file as an xarray Dataset object. Recall that you can review the data in a notebook by using `print(ds)` or `ds`. \n", + "\n", + "Using xarray to retrieve the aforementioned NetCDF basics is straightforwared. In general,\n", + "* Dataset: `ds`\n", + "* Data array: `ds.variables`\n", + "* Variable: `ds.ds.variables`\n", + "* Dimenssion: `ds.dims`\n", + "* Coordinate: `ds.coords`\n", + "* Data Type: `type`\n", + "* Meta Data (Attributes)\n", + "\n", + "Also, here are some references if you are new to xarray\n", + "* [Xarray in 45 minutes](https://tutorial.xarray.dev/overview/xarray-in-45-min.html)\n", + "* [Handling NetCDF Files using XArray for Absolute Beginners](https://towardsdatascience.com/handling-netcdf-files-using-xarray-for-absolute-beginners-111a8ab4463f)\n", + "\n", + "Try the following commands in action\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b3eac323", + "metadata": {}, + "outputs": [], + "source": [ + "ds = ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77ecf85d", + "metadata": {}, + "outputs": [], + "source": [ + "# Dataset \n", + "ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25e7de09", + "metadata": {}, + "outputs": [], + "source": [ + "# Data array, Variable\n", + "# Note: the info can be overwhelming. Do not attempt to grasp everything at the first glance.\n", + "ds.variables" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c41b67e", + "metadata": {}, + "outputs": [], + "source": [ + "# dimenssions\n", + "ds.dims" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "156f1dfc", + "metadata": {}, + "outputs": [], + "source": [ + "# coordinates\n", + "ds.coords" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "277d6064", + "metadata": {}, + "outputs": [], + "source": [ + "# Meta Data (Attributes)\n", + "ds.attrs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d334681f", + "metadata": {}, + "outputs": [], + "source": [ + "# type\n", + "print(type(ds))\n", + "print(type(ds.variables))\n", + "print(type(ds.dims))\n", + "print(type(ds.coords))\n", + "print(type(ds.attrs))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "643399d6", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: retrieve variable names only\n", + "list(ds.variables)" + ] + }, + { + "cell_type": "markdown", + "id": "1cf36878", + "metadata": {}, + "source": [ + "#### Discussion: variable vs. coordinates vs. dimenssions. \n", + "From [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#):\n", + "\n", + "* Dimession: A dimension may be used to represent a real physical dimension, for example, time, latitude, longitude, or height. A dimension might also be used to index other quantities, for example station or model-run-number.\n", + "A netCDF dimension has both a name and a length. A dimension length is an arbitrary positive integer, except that one dimension in a netCDF dataset can have the length UNLIMITED.\n", + "\n", + "* Variables: Variables are used to store the bulk of the data in a netCDF dataset. A variable represents an array of values of the same type. A scalar value is treated as a 0-dimensional array. A variable has a name, a data type, and a shape described by its list of dimensions specified when the variable is created. A variable may also have associated attributes, which may be added, deleted or changed after the variable is created.\n", + "\n", + "* Coordinate (Variables): It is legal for a variable to have the same name as a dimension. Such variables have no special meaning to the netCDF library. However there is a convention that such variables should be treated in a special way by software using this library.\n", + "A variable with the same name as a dimension is called a coordinate variable. It typically defines a physical coordinate corresponding to that dimension.\n", + "\n", + "\n", + "Tips: By definition, coordinates and dimenssions are also variable. For example ds.time is a coordinate but it is also a special variable. But in practice, when we talk about variable, it implies regular/non-coordinate variable. We will use this convention for the remaining notebook. " + ] + }, + { + "cell_type": "markdown", + "id": "d47120f9", + "metadata": {}, + "source": [ + "### Xarray Variable\n", + "\n", + "\n", + "We can use `ds.variables` to access varialbes of a dataset. But many times viewing print-out the whole datasets can be overwhemling. Instead, we would work on the individual variable (also called Xarray data array.) using the `ds[var_name]` syntax, where `var_name` is the name of the variable. See the following examples in action.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5a57e136", + "metadata": {}, + "outputs": [], + "source": [ + "print(type(ds[\"time\"]))\n", + "ds[\"time\"]" + ] + }, + { + "cell_type": "markdown", + "id": "51ec643e", + "metadata": {}, + "source": [ + "#### Variable properties\n", + "For individual varible, the following properties are mostly used with the assocaited xarray method to retrieve them. (assuming var = ds[var_name])\n", + "* name: `var.name`\n", + "* data content: `var.data`\n", + "* attributes: `var.attrs`\n", + "* dimenstions: `var.dims`\n", + "* data type: `var.data.dtype`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a632a525", + "metadata": {}, + "outputs": [], + "source": [ + "var_name = \"time\"\n", + "var = ds[var_name]\n", + "\n", + "print(\"var.name: \\n\", var.name, \"\\n\")\n", + "print(\"var.data: \\n\", var.data, \"\\n\")\n", + "print(\"var.attrs: \\n\", var.attrs, \"\\n\")\n", + "print(\"var.dims: \\n\", var.dims, \"\\n\")\n", + "print(\"var.data.dtype: \\n\", var.data.dtype, \"\\n\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b256b07f", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: indivdual variable info table (collect and display variable info as a tabular format)\n", + "df_info = pd.DataFrame()\n", + "df_info[\"var_name\"] = list(ds.variables)\n", + "df_info[\"dims\"] = df_info.var_name.apply(lambda x: ds[x].dims)\n", + "df_info[\"is_dim\"] = df_info.var_name.apply(lambda x: x in ds.dims)\n", + "df_info[\"n_dim\"] = df_info.var_name.apply(lambda x: len(ds[x].dims))\n", + "df_info[\"attrs\"] = df_info.var_name.apply(lambda x: ds[x].attrs)\n", + "df_info[\"dtype\"] = df_info.var_name.apply(lambda x: ds[x].data.dtype)\n", + "df_info" + ] + }, + { + "cell_type": "markdown", + "id": "e6a36b96", + "metadata": {}, + "source": [ + "### Data cleaning/Preprocessing (skipped)\n", + "Data cleaning and preprocessing is an important stage in the the data analysis pipeline. However it will be out of the scope of this notebook. For more information about data cleaning and preprocessing basics, here are some references. \n", + "* [Xarray Fundamentals](https://earth-env-data-science.github.io/lectures/xarray/xarray.html)\n", + "* [Xarray Tutorial — Pangeo Gallery documentation](http://gallery.pangeo.io/repos/pangeo-data/pangeo-tutorial-gallery/xarray.html)\n", + "* [Pythonic Data Cleaning With pandas and NumPy](https://realpython.com/python-data-cleaning-numpy-pandas/)\n", + "* [Pandas - Cleaning Data](https://www.w3schools.com/python/pandas/pandas_cleaning.asp)" + ] + }, + { + "cell_type": "markdown", + "id": "ae6e1a69", + "metadata": {}, + "source": [ + "### Plotting\n", + "Data visualization (or plotting) is another important data analysis topic and deserves its own discussion. Here in this notebook we will only demonstrate basic tool for simple data visualization tasks.\n", + "\n", + "Here are some reference you might find useful:\n", + "* xarray plotting: https://docs.xarray.dev/en/stable/user-guide/plotting.html\n", + "* Atmospheric Community Toolkit (ACT): https://arm-doe.github.io/ACT/index.html\n", + "\n", + "Note: this notebook is auto-generated using a template. It uses a general idea to select variable(s) to plot and is not customized to each indivdual notebook. Feel free to change the variables in intrrests, especially certain figure is failed to plot." + ] + }, + { + "cell_type": "markdown", + "id": "7e417fb2", + "metadata": {}, + "source": [ + "#### 1-dimenssional basic time series plot\n", + "\n", + "For the following plot we would like to find variables such that\n", + "* it has one and only one dimession\n", + "* \"time\" is its coordinate variable\n", + "* it is not a dimenssion itself,\n", + "* it is not a special variable with substrings within [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "\n", + "Please see the following example in action. (For more details about pandas filtering, please see the following references.)\n", + "* [pandas: multiple conditions while indexing data frame](https://stackoverflow.com/questions/22591174/pandas-multiple-conditions-while-indexing-data-frame-unexpected-behavior)\n", + "* [pandas.DataFrame.apply](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.apply.html)\n", + "* [How to test if a string contains one of the substrings in a list...](https://stackoverflow.com/questions/26577516/how-to-test-if-a-string-contains-one-of-the-substrings-in-a-list-in-pandas" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30edac2d", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==1, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter = df_info[(df_info.n_dim == 1) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4777a659", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_1d = df_filter.var_name.values[0]\n", + " var_1d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb5ae985", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " ds[var_1d].plot()\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "9e41066e", + "metadata": {}, + "source": [ + "#### 2-dimenssional basic plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09bd4adc", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==2, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_2 = df_info[(df_info.n_dim == 2) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2cdea1a9", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_2d = df_filter_2.var_name.values[0]\n", + " var_2d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5f48879", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " print(ds[var_2d].dims)\n", + " # ds[var_2d].plot()\n", + "\n", + " # conventionally, use \"time\" as x-axis\n", + " ds[var_2d].plot(x=\"time\")\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "7554b2a6", + "metadata": {}, + "source": [ + "#### qc-plotting (optional)\n", + "\n", + "Note: act qc plotting has more strict requirement, one of them is the associated qc variable need to have \"flag_masks\" attributes. Which is added by using ds = act.io.armfiles.read_netcdf(files_list), then ds.clean.cleanup()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71c2096f", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " ds_act = act.io.armfiles.read_netcdf(full_path)\n", + " print(type(ds_act))\n", + " ds_act.clean.cleanup()\n", + "\n", + " # or \n", + " # ds.clean.cleanup()\n", + "except Exception as e:\n", + " print(\"ERROR\", e)\n", + " ds_act = ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "844f8505", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter valid variables for ACT qc plotting\n", + "condition = (\"qc_\" + df_info.var_name).apply(lambda x: ds_act[x].attrs.get(\"flag_masks\") is not None \n", + " if x in list(ds.variables) else False)\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_3 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings))) &\n", + " condition\n", + " ]\n", + "df_filter_3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd38cc6", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "try:\n", + " qc_variable = df_filter_3.var_name.values[0]\n", + " print(qc_variable)\n", + "except Exception as e:\n", + " print(e)\n", + " \n", + "try:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + " qc_ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "d76e4d27", + "metadata": {}, + "source": [ + "#### bonus: choose variables to plot from a dropdown menu " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb733804", + "metadata": {}, + "outputs": [], + "source": [ + "# Valid variables filtering\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_4 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82d5c20d", + "metadata": {}, + "outputs": [], + "source": [ + "# example 1: using xarray plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2fc9aaa4", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# fig, ax = plt.subplots(figsize=(10, 4))\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "# fig.clear() # Remove old lines from plot and plot new one\n", + "# if len(ds[var].dims)==2:\n", + "# ds[var].plot(x=\"time\", add_colorbar=False)\n", + "# else:\n", + "# ds[var].plot()\n", + "# plt.grid()\n", + "# plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fabdd802", + "metadata": {}, + "outputs": [], + "source": [ + "# example 2: using act plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "962b4186", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "\n", + "\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "\n", + "# i_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + "# i_display.add_subplots((1,), figsize=(10, 4))\n", + "# ax = i_display.plot(var, subplot_index=(0,), set_title=f\"{var} ({ds_act[var].attrs['long_name']})\",)\n", + "\n", + "# ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "# ax.grid()\n", + "# plt.show()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/ARSCL/arscl1cloth.c1.ipynb b/VAPs/quicklook/ARSCL/arscl1cloth.c1.ipynb new file mode 100644 index 00000000..21e2bc45 --- /dev/null +++ b/VAPs/quicklook/ARSCL/arscl1cloth.c1.ipynb @@ -0,0 +1,339 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# ARSCL1CLOTH.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/arscl) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'arscl1cloth'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2011-03-23', 'facility': 'C1', 'site': 'nsa', 'start_date': '1998-03-25'}, {'end_date': '2011-01-04', 'facility': 'C1', 'site': 'sgp', 'start_date': '1996-11-08'}, {'end_date': '2011-03-07', 'facility': 'C1', 'site': 'twp', 'start_date': '1999-07-01'}, {'end_date': '2009-02-14', 'facility': 'C2', 'site': 'twp', 'start_date': '1998-11-01'}, {'end_date': '2011-02-28', 'facility': 'C3', 'site': 'twp', 'start_date': '2003-01-01'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2011-01-03'\n", + "date_end = '2011-01-04'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['Reflectivity', 'ReflectivityNoClutter', 'ReflectivityBestEstimate']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'RadarArtifacts'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'Reflectivity'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/ARSCL/arsclbnd1cloth.c1.ipynb b/VAPs/quicklook/ARSCL/arsclbnd1cloth.c1.ipynb new file mode 100644 index 00000000..c275fc7d --- /dev/null +++ b/VAPs/quicklook/ARSCL/arsclbnd1cloth.c1.ipynb @@ -0,0 +1,339 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# ARSCLBND1CLOTH.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/arscl) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'arsclbnd1cloth'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2011-03-23', 'facility': 'C1', 'site': 'nsa', 'start_date': '1998-03-25'}, {'end_date': '2011-01-04', 'facility': 'C1', 'site': 'sgp', 'start_date': '1996-11-08'}, {'end_date': '2011-03-07', 'facility': 'C1', 'site': 'twp', 'start_date': '1999-07-01'}, {'end_date': '2009-02-14', 'facility': 'C2', 'site': 'twp', 'start_date': '1998-11-01'}, {'end_date': '2011-02-28', 'facility': 'C3', 'site': 'twp', 'start_date': '2003-01-01'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2011-01-03'\n", + "date_end = '2011-01-04'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['CloudBaseBestEstimate']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'CloudLayerTopHeightMplCamp'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'CloudBaseBestEstimate'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/ASDBE-AIR/ASDBE-AIR_tutorial.ipynb b/VAPs/quicklook/ASDBE-AIR/ASDBE-AIR_tutorial.ipynb new file mode 100644 index 00000000..4b3c7f0d --- /dev/null +++ b/VAPs/quicklook/ASDBE-AIR/ASDBE-AIR_tutorial.ipynb @@ -0,0 +1,838 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# AAFMERGEDAEROSOLSD.C1 Notebook\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/asdbe-air) for more information about this vap." + ] + }, + { + "cell_type": "markdown", + "id": "97097763", + "metadata": {}, + "source": [ + "In this notebook, we demonstrate the workflow to explore ARM vap data (using aafmergedaerosolsd as an example.) Value-added products (VAPs) are higher-order data products that have been analyzed and processed to ease scientists’ use of ARM data in atmospheric research and global climate models. \n", + "Here is the main content we will cover." + ] + }, + { + "cell_type": "markdown", + "id": "eddec40f", + "metadata": {}, + "source": [ + "# Table of Content\n", + "## Access the data\n", + "* How to retrieve the data\n", + "* Data path and file name conventions\n", + "* Load data\n", + "## Explore the data\n", + "* NetCDF Data structure\n", + "* Xarray essentials\n", + "* Xarray Variable\n", + "## Plot the data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "import random\n", + "\n", + "import glob\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "2372d5be", + "metadata": {}, + "source": [ + "## Access the data" + ] + }, + { + "cell_type": "markdown", + "id": "5b0d9684", + "metadata": {}, + "source": [ + "### How to retrieve the data\n", + "We assume the path \"/data/archive\" is available where you are running this notebook. You can use `os.path.exists(\"/data/archive\")` to verify if the path exists at your machine.\n", + "\n", + "\n", + "### Data path and file name conventions\n", + "There several common terminologies regarding ARM data, for example, data-stream-name, data-level, site, facility, instrument, etc. (For more details, please see [ARM Data File Standards Version 1.2](https://www.google.com/search?q=arm+datastream+facility+cite+definition&rlz=1C1GCEJ_enUS1029US1029&ei=hb41ZICrBPukqtsPvMODIA&ved=0ahUKEwjAgeiS0aL-AhV7kmoFHbzhAAQQ4dUDCBA&uact=5&oq=arm+datastream+facility+cite+definition&gs_lcp=Cgxnd3Mtd2l6LXNlcnAQAzIFCAAQogQyBQgAEKIEMgUIABCiBDIFCAAQogQyBQgAEKIEOgoIABBHENYEELADOgoIIRCgARDDBBAKSgQIQRgAUJ4DWIoOYO0PaAFwAXgAgAGMAYgBuQmSAQMyLjmYAQCgAQHIAQjAAQE&sclient=gws-wiz-serp).) \n", + "\n", + "For example, this notebook is called `aafmergedaerosolsd.c1`, where `aafmergedaerosolsd` is the \"datastream name\", and the `{process.ds_class_level}}` is the \"data level\".\n", + "\n", + "This datastream also contains site `ena` and facility `F1`. (Note: individual datastream might have multiple site-facility pairs.)\n", + "In such a case, the data of this data-stream is stored at `/data/archive/ena/enaaafmergedaerosolsdF1.c1`, which is in the format of `//.`. We can use the following method to assign the data-stream directory `datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )`\n", + "\n", + "The data files under datastream_dir also follows naming conventions. But once reach the datastream_dir level, the most import file naming convention to differentiate the files is \"yyyyMMdd.hhmmss\", which comes handy to filter out files based on datetime. For example, we can use `glob.glob(f'{datastream_dir}/*.200709*.*')` to filter files in 2007 September.\n", + "\n", + "(Note: refer to the https://adc.arm.gov/discovery/#/ and https://adc.arm.gov/solr8/metadata/select API to explore ARM datastream and assoicated available sites and facilities\n", + "\n", + "Please see the following examples in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d7e9eb85", + "metadata": {}, + "outputs": [], + "source": [ + "# Verify if DATA_DIR path exists\n", + "DATA_DIR = \"/data/archive\"\n", + "os.path.exists(DATA_DIR)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "586993fd", + "metadata": {}, + "outputs": [], + "source": [ + "# Speicify datastream_dir following the path conventions and check its existence\n", + "DATASTREAM_NAME = \"aafmergedaerosolsd\"\n", + "DATA_LEVEL = \"c1\"\n", + "site = \"ena\"\n", + "facility = \"F1\"\n", + "datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )\n", + "print(datastream_dir)\n", + "print(os.path.exists(datastream_dir))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0742f7c1", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: list 5 (random) files under datastream_dir\n", + "files = os.listdir(datastream_dir)\n", + "files[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39b98a36", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: get most recent file\n", + "list_of_files = glob.glob(f\"{datastream_dir}/*\") # * means all if need specific format then *.csv\n", + "latest_file = max(list_of_files, key=os.path.getctime)\n", + "latest_file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "902d514e", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus sort datastream files based on datetime\n", + "files = os.listdir(datastream_dir)\n", + "file_sorted = files.copy()\n", + "file_sorted.sort() \n", + "print(file_sorted[:5])\n", + "\n", + "# to reverse\n", + "file_sorted_reverse = files.copy() \n", + "file_sorted_reverse.sort(reverse=True)\n", + "print(file_sorted_reverse[:5])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec5923b2", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: pattern matching\n", + "# filter the 200709** files under datastream_dir\n", + "files_filter = glob.glob(f'{datastream_dir}/*.200709*.*')\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c4a4aa18", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: use armnotebook_utils.file_filter (TODO) to filter files based on datastream info \n" + ] + }, + { + "cell_type": "markdown", + "id": "cfeb9efc", + "metadata": {}, + "source": [ + "### Load data\n", + "The arm data is stored in [NetCDF](https://en.wikipedia.org/wiki/NetCDF#:~:text=%22NetCDF%20(network%20Common%20Data%20Form,format%20for%20representing%20scientific%20data.) format. We can use xarray's `open_dataset` method to load single file, or `open_mfdataset` to open multiple files. (Note: the latter will still return a single xarray dataset object by combining multiple files.)\n", + "\n", + "Note: open_dataset keeps the file handle open and lazy loads its contents. All parameters are passed directly to open_dataset. It is a preferable method over load_dataset for memory-effiency. (more details, please see [xarray.open_dataset](https://docs.xarray.dev/en/stable/generated/xarray.open_dataset.html))\n", + "\n", + "See the following example in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a440a329", + "metadata": {}, + "outputs": [], + "source": [ + "# open single file\n", + "full_path = latest_file\n", + "print(full_path)\n", + "ds_single = xr.open_dataset(full_path)\n", + "ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0143a3d", + "metadata": {}, + "outputs": [], + "source": [ + "print(ds_single)\n", + "print(type(ds_single))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c0f8939", + "metadata": {}, + "outputs": [], + "source": [ + "# open multiple files\n", + "n_files = 3\n", + "full_paths = [os.path.join(datastream_dir, f_path) for f_path in file_sorted_reverse[:n_files]]\n", + "print(full_paths)\n", + "try: # Note: sometimes multiple files cannot be merged, so we used try except here.\n", + " ds_mutiple = xr.open_mfdataset(full_paths)\n", + " # ds_mutiple\n", + " print(type(ds_mutiple))\n", + " print(ds_mutiple)\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "3fa59943", + "metadata": {}, + "source": [ + "## Explore the data" + ] + }, + { + "cell_type": "markdown", + "id": "571f69b9", + "metadata": {}, + "source": [ + "### NetCDF Data structure \n", + "\n", + "\n", + "(If you are confident with NetCDF basics and xarray essentials, feel free to skip this session.)\n", + "Before we dive into data exploration, there are some eseential concpets we should be familiar with (shown below): \n", + "* Dataset\n", + "* Data array\n", + "* Variable\n", + "* Dimenssion\n", + "* Coordinate\n", + "* Data Type\n", + "* Meta Data (Attributes)\n", + "\n", + "We will not go into details about NetCDF basics and here are some references you might find helpful\n", + "* [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#)\n", + "* [Network Common Data Form (NetCDF)](https://www.unidata.ucar.edu/software/netcdf/) and [A Brief History of (netCDF) Time](https://www.unidata.ucar.edu/software/netcdf/time/recs.html)\n", + "\n", + "\n", + "\n", + "### Xarray essentials\n", + "Earlier we introduced the xarray pacakge and used `open_dataset` and `open_mfdataset` to retrieve NetCDF data file as an xarray Dataset object. Recall that you can review the data in a notebook by using `print(ds)` or `ds`. \n", + "\n", + "Using xarray to retrieve the aforementioned NetCDF basics is straightforwared. In general,\n", + "* Dataset: `ds`\n", + "* Data array: `ds.variables`\n", + "* Variable: `ds.ds.variables`\n", + "* Dimenssion: `ds.dims`\n", + "* Coordinate: `ds.coords`\n", + "* Data Type: `type`\n", + "* Meta Data (Attributes)\n", + "\n", + "Also, here are some references if you are new to xarray\n", + "* [Xarray in 45 minutes](https://tutorial.xarray.dev/overview/xarray-in-45-min.html)\n", + "* [Handling NetCDF Files using XArray for Absolute Beginners](https://towardsdatascience.com/handling-netcdf-files-using-xarray-for-absolute-beginners-111a8ab4463f)\n", + "\n", + "Try the following commands in action\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b3eac323", + "metadata": {}, + "outputs": [], + "source": [ + "ds = ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77ecf85d", + "metadata": {}, + "outputs": [], + "source": [ + "# Dataset \n", + "ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25e7de09", + "metadata": {}, + "outputs": [], + "source": [ + "# Data array, Variable\n", + "# Note: the info can be overwhelming. Do not attempt to grasp everything at the first glance.\n", + "ds.variables" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c41b67e", + "metadata": {}, + "outputs": [], + "source": [ + "# dimenssions\n", + "ds.dims" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "156f1dfc", + "metadata": {}, + "outputs": [], + "source": [ + "# coordinates\n", + "ds.coords" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "277d6064", + "metadata": {}, + "outputs": [], + "source": [ + "# Meta Data (Attributes)\n", + "ds.attrs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d334681f", + "metadata": {}, + "outputs": [], + "source": [ + "# type\n", + "print(type(ds))\n", + "print(type(ds.variables))\n", + "print(type(ds.dims))\n", + "print(type(ds.coords))\n", + "print(type(ds.attrs))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "643399d6", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: retrieve variable names only\n", + "list(ds.variables)" + ] + }, + { + "cell_type": "markdown", + "id": "1cf36878", + "metadata": {}, + "source": [ + "#### Discussion: variable vs. coordinates vs. dimenssions. \n", + "From [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#):\n", + "\n", + "* Dimession: A dimension may be used to represent a real physical dimension, for example, time, latitude, longitude, or height. A dimension might also be used to index other quantities, for example station or model-run-number.\n", + "A netCDF dimension has both a name and a length. A dimension length is an arbitrary positive integer, except that one dimension in a netCDF dataset can have the length UNLIMITED.\n", + "\n", + "* Variables: Variables are used to store the bulk of the data in a netCDF dataset. A variable represents an array of values of the same type. A scalar value is treated as a 0-dimensional array. A variable has a name, a data type, and a shape described by its list of dimensions specified when the variable is created. A variable may also have associated attributes, which may be added, deleted or changed after the variable is created.\n", + "\n", + "* Coordinate (Variables): It is legal for a variable to have the same name as a dimension. Such variables have no special meaning to the netCDF library. However there is a convention that such variables should be treated in a special way by software using this library.\n", + "A variable with the same name as a dimension is called a coordinate variable. It typically defines a physical coordinate corresponding to that dimension.\n", + "\n", + "\n", + "Tips: By definition, coordinates and dimenssions are also variable. For example ds.time is a coordinate but it is also a special variable. But in practice, when we talk about variable, it implies regular/non-coordinate variable. We will use this convention for the remaining notebook. " + ] + }, + { + "cell_type": "markdown", + "id": "d47120f9", + "metadata": {}, + "source": [ + "### Xarray Variable\n", + "\n", + "\n", + "We can use `ds.variables` to access varialbes of a dataset. But many times viewing print-out the whole datasets can be overwhemling. Instead, we would work on the individual variable (also called Xarray data array.) using the `ds[var_name]` syntax, where `var_name` is the name of the variable. See the following examples in action.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5a57e136", + "metadata": {}, + "outputs": [], + "source": [ + "print(type(ds[\"time\"]))\n", + "ds[\"time\"]" + ] + }, + { + "cell_type": "markdown", + "id": "51ec643e", + "metadata": {}, + "source": [ + "#### Variable properties\n", + "For individual varible, the following properties are mostly used with the assocaited xarray method to retrieve them. (assuming var = ds[var_name])\n", + "* name: `var.name`\n", + "* data content: `var.data`\n", + "* attributes: `var.attrs`\n", + "* dimenstions: `var.dims`\n", + "* data type: `var.data.dtype`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a632a525", + "metadata": {}, + "outputs": [], + "source": [ + "var_name = \"time\"\n", + "var = ds[var_name]\n", + "\n", + "print(\"var.name: \\n\", var.name, \"\\n\")\n", + "print(\"var.data: \\n\", var.data, \"\\n\")\n", + "print(\"var.attrs: \\n\", var.attrs, \"\\n\")\n", + "print(\"var.dims: \\n\", var.dims, \"\\n\")\n", + "print(\"var.data.dtype: \\n\", var.data.dtype, \"\\n\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b256b07f", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: indivdual variable info table (collect and display variable info as a tabular format)\n", + "df_info = pd.DataFrame()\n", + "df_info[\"var_name\"] = list(ds.variables)\n", + "df_info[\"dims\"] = df_info.var_name.apply(lambda x: ds[x].dims)\n", + "df_info[\"is_dim\"] = df_info.var_name.apply(lambda x: x in ds.dims)\n", + "df_info[\"n_dim\"] = df_info.var_name.apply(lambda x: len(ds[x].dims))\n", + "df_info[\"attrs\"] = df_info.var_name.apply(lambda x: ds[x].attrs)\n", + "df_info[\"dtype\"] = df_info.var_name.apply(lambda x: ds[x].data.dtype)\n", + "df_info" + ] + }, + { + "cell_type": "markdown", + "id": "e6a36b96", + "metadata": {}, + "source": [ + "### Data cleaning/Preprocessing (skipped)\n", + "Data cleaning and preprocessing is an important stage in the the data analysis pipeline. However it will be out of the scope of this notebook. For more information about data cleaning and preprocessing basics, here are some references. \n", + "* [Xarray Fundamentals](https://earth-env-data-science.github.io/lectures/xarray/xarray.html)\n", + "* [Xarray Tutorial — Pangeo Gallery documentation](http://gallery.pangeo.io/repos/pangeo-data/pangeo-tutorial-gallery/xarray.html)\n", + "* [Pythonic Data Cleaning With pandas and NumPy](https://realpython.com/python-data-cleaning-numpy-pandas/)\n", + "* [Pandas - Cleaning Data](https://www.w3schools.com/python/pandas/pandas_cleaning.asp)" + ] + }, + { + "cell_type": "markdown", + "id": "ae6e1a69", + "metadata": {}, + "source": [ + "### Plotting\n", + "Data visualization (or plotting) is another important data analysis topic and deserves its own discussion. Here in this notebook we will only demonstrate basic tool for simple data visualization tasks.\n", + "\n", + "Here are some reference you might find useful:\n", + "* xarray plotting: https://docs.xarray.dev/en/stable/user-guide/plotting.html\n", + "* Atmospheric Community Toolkit (ACT): https://arm-doe.github.io/ACT/index.html\n", + "\n", + "Note: this notebook is auto-generated using a template. It uses a general idea to select variable(s) to plot and is not customized to each indivdual notebook. Feel free to change the variables in intrrests, especially certain figure is failed to plot." + ] + }, + { + "cell_type": "markdown", + "id": "7e417fb2", + "metadata": {}, + "source": [ + "#### 1-dimenssional basic time series plot\n", + "\n", + "For the following plot we would like to find variables such that\n", + "* it has one and only one dimession\n", + "* \"time\" is its coordinate variable\n", + "* it is not a dimenssion itself,\n", + "* it is not a special variable with substrings within [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "\n", + "Please see the following example in action. (For more details about pandas filtering, please see the following references.)\n", + "* [pandas: multiple conditions while indexing data frame](https://stackoverflow.com/questions/22591174/pandas-multiple-conditions-while-indexing-data-frame-unexpected-behavior)\n", + "* [pandas.DataFrame.apply](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.apply.html)\n", + "* [How to test if a string contains one of the substrings in a list...](https://stackoverflow.com/questions/26577516/how-to-test-if-a-string-contains-one-of-the-substrings-in-a-list-in-pandas" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30edac2d", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==1, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter = df_info[(df_info.n_dim == 1) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4777a659", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_1d = df_filter.var_name.values[0]\n", + " var_1d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb5ae985", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " ds[var_1d].plot()\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "9e41066e", + "metadata": {}, + "source": [ + "#### 2-dimenssional basic plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09bd4adc", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==2, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_2 = df_info[(df_info.n_dim == 2) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2cdea1a9", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_2d = df_filter_2.var_name.values[0]\n", + " var_2d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5f48879", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " print(ds[var_2d].dims)\n", + " # ds[var_2d].plot()\n", + "\n", + " # conventionally, use \"time\" as x-axis\n", + " ds[var_2d].plot(x=\"time\")\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "7554b2a6", + "metadata": {}, + "source": [ + "#### qc-plotting (optional)\n", + "\n", + "Note: act qc plotting has more strict requirement, one of them is the associated qc variable need to have \"flag_masks\" attributes. Which is added by using ds = act.io.armfiles.read_netcdf(files_list), then ds.clean.cleanup()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71c2096f", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " ds_act = act.io.armfiles.read_netcdf(full_path)\n", + " print(type(ds_act))\n", + " ds_act.clean.cleanup()\n", + "\n", + " # or \n", + " # ds.clean.cleanup()\n", + "except Exception as e:\n", + " print(\"ERROR\", e)\n", + " ds_act = ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "844f8505", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter valid variables for ACT qc plotting\n", + "condition = (\"qc_\" + df_info.var_name).apply(lambda x: ds_act[x].attrs.get(\"flag_masks\") is not None \n", + " if x in list(ds.variables) else False)\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_3 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings))) &\n", + " condition\n", + " ]\n", + "df_filter_3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd38cc6", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "try:\n", + " qc_variable = df_filter_3.var_name.values[0]\n", + " print(qc_variable)\n", + "except Exception as e:\n", + " print(e)\n", + " \n", + "try:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + " qc_ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "d76e4d27", + "metadata": {}, + "source": [ + "#### bonus: choose variables to plot from a dropdown menu " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb733804", + "metadata": {}, + "outputs": [], + "source": [ + "# Valid variables filtering\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_4 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82d5c20d", + "metadata": {}, + "outputs": [], + "source": [ + "# example 1: using xarray plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2fc9aaa4", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# fig, ax = plt.subplots(figsize=(10, 4))\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "# fig.clear() # Remove old lines from plot and plot new one\n", + "# if len(ds[var].dims)==2:\n", + "# ds[var].plot(x=\"time\", add_colorbar=False)\n", + "# else:\n", + "# ds[var].plot()\n", + "# plt.grid()\n", + "# plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fabdd802", + "metadata": {}, + "outputs": [], + "source": [ + "# example 2: using act plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "962b4186", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "\n", + "\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "\n", + "# i_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + "# i_display.add_subplots((1,), figsize=(10, 4))\n", + "# ax = i_display.plot(var, subplot_index=(0,), set_title=f\"{var} ({ds_act[var].attrs['long_name']})\",)\n", + "\n", + "# ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "# ax.grid()\n", + "# plt.show()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/ASDBE-AIR/aafmergedaerosolsd.c1.ipynb b/VAPs/quicklook/ASDBE-AIR/aafmergedaerosolsd.c1.ipynb new file mode 100644 index 00000000..e1e9ec56 --- /dev/null +++ b/VAPs/quicklook/ASDBE-AIR/aafmergedaerosolsd.c1.ipynb @@ -0,0 +1,297 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# AAFMERGEDAEROSOLSD.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/asdbe-air) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'aafmergedaerosolsd'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2018-02-19', 'facility': 'F1', 'site': 'ena', 'start_date': '2017-06-21'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'ena', 'F1' )\n", + "\n", + "date_start = '2018-02-17'\n", + "date_end = '2018-02-19'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['number_concentration']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'number_concentration'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/BAEBBR/30baebbr.c1.ipynb b/VAPs/quicklook/BAEBBR/30baebbr.c1.ipynb new file mode 100644 index 00000000..f6fdf996 --- /dev/null +++ b/VAPs/quicklook/BAEBBR/30baebbr.c1.ipynb @@ -0,0 +1,339 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# 30BAEBBR.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/baebbr) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = '30baebbr'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2023-09-28', 'facility': 'E11', 'site': 'sgp', 'start_date': '2016-08-04'}, {'end_date': '2023-11-30', 'facility': 'E12', 'site': 'sgp', 'start_date': '1993-09-29'}, {'end_date': '2023-11-30', 'facility': 'E13', 'site': 'sgp', 'start_date': '1993-07-20'}, {'end_date': '2023-09-28', 'facility': 'E15', 'site': 'sgp', 'start_date': '1993-07-11'}, {'end_date': '2009-11-17', 'facility': 'E18', 'site': 'sgp', 'start_date': '1997-09-10'}, {'end_date': '2011-09-18', 'facility': 'E19', 'site': 'sgp', 'start_date': '1997-05-30'}, {'end_date': '2011-11-16', 'facility': 'E20', 'site': 'sgp', 'start_date': '1993-07-06'}, {'end_date': '2009-12-01', 'facility': 'E22', 'site': 'sgp', 'start_date': '1993-07-04'}, {'end_date': '2002-04-08', 'facility': 'E25', 'site': 'sgp', 'start_date': '1997-08-10'}, {'end_date': '2009-12-17', 'facility': 'E26', 'site': 'sgp', 'start_date': '1993-07-05'}, {'end_date': '2009-12-04', 'facility': 'E27', 'site': 'sgp', 'start_date': '2003-05-07'}, {'end_date': '2009-10-20', 'facility': 'E2', 'site': 'sgp', 'start_date': '1997-05-22'}, {'end_date': '2023-11-30', 'facility': 'E32', 'site': 'sgp', 'start_date': '2011-09-28'}, {'end_date': '2023-09-28', 'facility': 'E34', 'site': 'sgp', 'start_date': '2011-09-02'}, {'end_date': '2023-09-23', 'facility': 'E35', 'site': 'sgp', 'start_date': '2011-10-05'}, {'end_date': '2023-09-28', 'facility': 'E36', 'site': 'sgp', 'start_date': '2011-09-28'}, {'end_date': '2023-11-30', 'facility': 'E39', 'site': 'sgp', 'start_date': '2015-09-30'}, {'end_date': '2023-09-28', 'facility': 'E40', 'site': 'sgp', 'start_date': '2015-10-15'}, {'end_date': '2011-09-25', 'facility': 'E4', 'site': 'sgp', 'start_date': '1993-07-13'}, {'end_date': '2011-11-13', 'facility': 'E7', 'site': 'sgp', 'start_date': '1993-10-04'}, {'end_date': '2009-11-10', 'facility': 'E8', 'site': 'sgp', 'start_date': '1993-07-12'}, {'end_date': '2023-09-28', 'facility': 'E9', 'site': 'sgp', 'start_date': '1993-07-11'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'E11' )\n", + "\n", + "date_start = '2023-09-26'\n", + "date_end = '2023-09-28'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['be_latent_heat_flux', 'be_sensible_heat_flux', 'net_radiation']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'aerodynamic_latent_heat_flux'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'be_latent_heat_flux'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/BAEBBR/BAEBBR_tutorial.ipynb b/VAPs/quicklook/BAEBBR/BAEBBR_tutorial.ipynb new file mode 100644 index 00000000..983b27d3 --- /dev/null +++ b/VAPs/quicklook/BAEBBR/BAEBBR_tutorial.ipynb @@ -0,0 +1,838 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# 30BAEBBR.C1 Notebook\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/baebbr) for more information about this vap." + ] + }, + { + "cell_type": "markdown", + "id": "97097763", + "metadata": {}, + "source": [ + "In this notebook, we demonstrate the workflow to explore ARM vap data (using 30baebbr as an example.) Value-added products (VAPs) are higher-order data products that have been analyzed and processed to ease scientists’ use of ARM data in atmospheric research and global climate models. \n", + "Here is the main content we will cover." + ] + }, + { + "cell_type": "markdown", + "id": "eddec40f", + "metadata": {}, + "source": [ + "# Table of Content\n", + "## Access the data\n", + "* How to retrieve the data\n", + "* Data path and file name conventions\n", + "* Load data\n", + "## Explore the data\n", + "* NetCDF Data structure\n", + "* Xarray essentials\n", + "* Xarray Variable\n", + "## Plot the data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "import random\n", + "\n", + "import glob\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "2372d5be", + "metadata": {}, + "source": [ + "## Access the data" + ] + }, + { + "cell_type": "markdown", + "id": "5b0d9684", + "metadata": {}, + "source": [ + "### How to retrieve the data\n", + "We assume the path \"/data/archive\" is available where you are running this notebook. You can use `os.path.exists(\"/data/archive\")` to verify if the path exists at your machine.\n", + "\n", + "\n", + "### Data path and file name conventions\n", + "There several common terminologies regarding ARM data, for example, data-stream-name, data-level, site, facility, instrument, etc. (For more details, please see [ARM Data File Standards Version 1.2](https://www.google.com/search?q=arm+datastream+facility+cite+definition&rlz=1C1GCEJ_enUS1029US1029&ei=hb41ZICrBPukqtsPvMODIA&ved=0ahUKEwjAgeiS0aL-AhV7kmoFHbzhAAQQ4dUDCBA&uact=5&oq=arm+datastream+facility+cite+definition&gs_lcp=Cgxnd3Mtd2l6LXNlcnAQAzIFCAAQogQyBQgAEKIEMgUIABCiBDIFCAAQogQyBQgAEKIEOgoIABBHENYEELADOgoIIRCgARDDBBAKSgQIQRgAUJ4DWIoOYO0PaAFwAXgAgAGMAYgBuQmSAQMyLjmYAQCgAQHIAQjAAQE&sclient=gws-wiz-serp).) \n", + "\n", + "For example, this notebook is called `30baebbr.c1`, where `30baebbr` is the \"datastream name\", and the `{process.ds_class_level}}` is the \"data level\".\n", + "\n", + "This datastream also contains site `sgp` and facility `E11`. (Note: individual datastream might have multiple site-facility pairs.)\n", + "In such a case, the data of this data-stream is stored at `/data/archive/sgp/sgp30baebbrE11.c1`, which is in the format of `//.`. We can use the following method to assign the data-stream directory `datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )`\n", + "\n", + "The data files under datastream_dir also follows naming conventions. But once reach the datastream_dir level, the most import file naming convention to differentiate the files is \"yyyyMMdd.hhmmss\", which comes handy to filter out files based on datetime. For example, we can use `glob.glob(f'{datastream_dir}/*.200709*.*')` to filter files in 2007 September.\n", + "\n", + "(Note: refer to the https://adc.arm.gov/discovery/#/ and https://adc.arm.gov/solr8/metadata/select API to explore ARM datastream and assoicated available sites and facilities\n", + "\n", + "Please see the following examples in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d7e9eb85", + "metadata": {}, + "outputs": [], + "source": [ + "# Verify if DATA_DIR path exists\n", + "DATA_DIR = \"/data/archive\"\n", + "os.path.exists(DATA_DIR)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "586993fd", + "metadata": {}, + "outputs": [], + "source": [ + "# Speicify datastream_dir following the path conventions and check its existence\n", + "DATASTREAM_NAME = \"30baebbr\"\n", + "DATA_LEVEL = \"c1\"\n", + "site = \"sgp\"\n", + "facility = \"E11\"\n", + "datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )\n", + "print(datastream_dir)\n", + "print(os.path.exists(datastream_dir))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0742f7c1", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: list 5 (random) files under datastream_dir\n", + "files = os.listdir(datastream_dir)\n", + "files[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39b98a36", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: get most recent file\n", + "list_of_files = glob.glob(f\"{datastream_dir}/*\") # * means all if need specific format then *.csv\n", + "latest_file = max(list_of_files, key=os.path.getctime)\n", + "latest_file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "902d514e", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus sort datastream files based on datetime\n", + "files = os.listdir(datastream_dir)\n", + "file_sorted = files.copy()\n", + "file_sorted.sort() \n", + "print(file_sorted[:5])\n", + "\n", + "# to reverse\n", + "file_sorted_reverse = files.copy() \n", + "file_sorted_reverse.sort(reverse=True)\n", + "print(file_sorted_reverse[:5])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec5923b2", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: pattern matching\n", + "# filter the 200709** files under datastream_dir\n", + "files_filter = glob.glob(f'{datastream_dir}/*.200709*.*')\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c4a4aa18", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: use armnotebook_utils.file_filter (TODO) to filter files based on datastream info \n" + ] + }, + { + "cell_type": "markdown", + "id": "cfeb9efc", + "metadata": {}, + "source": [ + "### Load data\n", + "The arm data is stored in [NetCDF](https://en.wikipedia.org/wiki/NetCDF#:~:text=%22NetCDF%20(network%20Common%20Data%20Form,format%20for%20representing%20scientific%20data.) format. We can use xarray's `open_dataset` method to load single file, or `open_mfdataset` to open multiple files. (Note: the latter will still return a single xarray dataset object by combining multiple files.)\n", + "\n", + "Note: open_dataset keeps the file handle open and lazy loads its contents. All parameters are passed directly to open_dataset. It is a preferable method over load_dataset for memory-effiency. (more details, please see [xarray.open_dataset](https://docs.xarray.dev/en/stable/generated/xarray.open_dataset.html))\n", + "\n", + "See the following example in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a440a329", + "metadata": {}, + "outputs": [], + "source": [ + "# open single file\n", + "full_path = latest_file\n", + "print(full_path)\n", + "ds_single = xr.open_dataset(full_path)\n", + "ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0143a3d", + "metadata": {}, + "outputs": [], + "source": [ + "print(ds_single)\n", + "print(type(ds_single))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c0f8939", + "metadata": {}, + "outputs": [], + "source": [ + "# open multiple files\n", + "n_files = 3\n", + "full_paths = [os.path.join(datastream_dir, f_path) for f_path in file_sorted_reverse[:n_files]]\n", + "print(full_paths)\n", + "try: # Note: sometimes multiple files cannot be merged, so we used try except here.\n", + " ds_mutiple = xr.open_mfdataset(full_paths)\n", + " # ds_mutiple\n", + " print(type(ds_mutiple))\n", + " print(ds_mutiple)\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "3fa59943", + "metadata": {}, + "source": [ + "## Explore the data" + ] + }, + { + "cell_type": "markdown", + "id": "571f69b9", + "metadata": {}, + "source": [ + "### NetCDF Data structure \n", + "\n", + "\n", + "(If you are confident with NetCDF basics and xarray essentials, feel free to skip this session.)\n", + "Before we dive into data exploration, there are some eseential concpets we should be familiar with (shown below): \n", + "* Dataset\n", + "* Data array\n", + "* Variable\n", + "* Dimenssion\n", + "* Coordinate\n", + "* Data Type\n", + "* Meta Data (Attributes)\n", + "\n", + "We will not go into details about NetCDF basics and here are some references you might find helpful\n", + "* [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#)\n", + "* [Network Common Data Form (NetCDF)](https://www.unidata.ucar.edu/software/netcdf/) and [A Brief History of (netCDF) Time](https://www.unidata.ucar.edu/software/netcdf/time/recs.html)\n", + "\n", + "\n", + "\n", + "### Xarray essentials\n", + "Earlier we introduced the xarray pacakge and used `open_dataset` and `open_mfdataset` to retrieve NetCDF data file as an xarray Dataset object. Recall that you can review the data in a notebook by using `print(ds)` or `ds`. \n", + "\n", + "Using xarray to retrieve the aforementioned NetCDF basics is straightforwared. In general,\n", + "* Dataset: `ds`\n", + "* Data array: `ds.variables`\n", + "* Variable: `ds.ds.variables`\n", + "* Dimenssion: `ds.dims`\n", + "* Coordinate: `ds.coords`\n", + "* Data Type: `type`\n", + "* Meta Data (Attributes)\n", + "\n", + "Also, here are some references if you are new to xarray\n", + "* [Xarray in 45 minutes](https://tutorial.xarray.dev/overview/xarray-in-45-min.html)\n", + "* [Handling NetCDF Files using XArray for Absolute Beginners](https://towardsdatascience.com/handling-netcdf-files-using-xarray-for-absolute-beginners-111a8ab4463f)\n", + "\n", + "Try the following commands in action\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b3eac323", + "metadata": {}, + "outputs": [], + "source": [ + "ds = ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77ecf85d", + "metadata": {}, + "outputs": [], + "source": [ + "# Dataset \n", + "ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25e7de09", + "metadata": {}, + "outputs": [], + "source": [ + "# Data array, Variable\n", + "# Note: the info can be overwhelming. Do not attempt to grasp everything at the first glance.\n", + "ds.variables" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c41b67e", + "metadata": {}, + "outputs": [], + "source": [ + "# dimenssions\n", + "ds.dims" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "156f1dfc", + "metadata": {}, + "outputs": [], + "source": [ + "# coordinates\n", + "ds.coords" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "277d6064", + "metadata": {}, + "outputs": [], + "source": [ + "# Meta Data (Attributes)\n", + "ds.attrs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d334681f", + "metadata": {}, + "outputs": [], + "source": [ + "# type\n", + "print(type(ds))\n", + "print(type(ds.variables))\n", + "print(type(ds.dims))\n", + "print(type(ds.coords))\n", + "print(type(ds.attrs))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "643399d6", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: retrieve variable names only\n", + "list(ds.variables)" + ] + }, + { + "cell_type": "markdown", + "id": "1cf36878", + "metadata": {}, + "source": [ + "#### Discussion: variable vs. coordinates vs. dimenssions. \n", + "From [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#):\n", + "\n", + "* Dimession: A dimension may be used to represent a real physical dimension, for example, time, latitude, longitude, or height. A dimension might also be used to index other quantities, for example station or model-run-number.\n", + "A netCDF dimension has both a name and a length. A dimension length is an arbitrary positive integer, except that one dimension in a netCDF dataset can have the length UNLIMITED.\n", + "\n", + "* Variables: Variables are used to store the bulk of the data in a netCDF dataset. A variable represents an array of values of the same type. A scalar value is treated as a 0-dimensional array. A variable has a name, a data type, and a shape described by its list of dimensions specified when the variable is created. A variable may also have associated attributes, which may be added, deleted or changed after the variable is created.\n", + "\n", + "* Coordinate (Variables): It is legal for a variable to have the same name as a dimension. Such variables have no special meaning to the netCDF library. However there is a convention that such variables should be treated in a special way by software using this library.\n", + "A variable with the same name as a dimension is called a coordinate variable. It typically defines a physical coordinate corresponding to that dimension.\n", + "\n", + "\n", + "Tips: By definition, coordinates and dimenssions are also variable. For example ds.time is a coordinate but it is also a special variable. But in practice, when we talk about variable, it implies regular/non-coordinate variable. We will use this convention for the remaining notebook. " + ] + }, + { + "cell_type": "markdown", + "id": "d47120f9", + "metadata": {}, + "source": [ + "### Xarray Variable\n", + "\n", + "\n", + "We can use `ds.variables` to access varialbes of a dataset. But many times viewing print-out the whole datasets can be overwhemling. Instead, we would work on the individual variable (also called Xarray data array.) using the `ds[var_name]` syntax, where `var_name` is the name of the variable. See the following examples in action.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5a57e136", + "metadata": {}, + "outputs": [], + "source": [ + "print(type(ds[\"time\"]))\n", + "ds[\"time\"]" + ] + }, + { + "cell_type": "markdown", + "id": "51ec643e", + "metadata": {}, + "source": [ + "#### Variable properties\n", + "For individual varible, the following properties are mostly used with the assocaited xarray method to retrieve them. (assuming var = ds[var_name])\n", + "* name: `var.name`\n", + "* data content: `var.data`\n", + "* attributes: `var.attrs`\n", + "* dimenstions: `var.dims`\n", + "* data type: `var.data.dtype`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a632a525", + "metadata": {}, + "outputs": [], + "source": [ + "var_name = \"time\"\n", + "var = ds[var_name]\n", + "\n", + "print(\"var.name: \\n\", var.name, \"\\n\")\n", + "print(\"var.data: \\n\", var.data, \"\\n\")\n", + "print(\"var.attrs: \\n\", var.attrs, \"\\n\")\n", + "print(\"var.dims: \\n\", var.dims, \"\\n\")\n", + "print(\"var.data.dtype: \\n\", var.data.dtype, \"\\n\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b256b07f", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: indivdual variable info table (collect and display variable info as a tabular format)\n", + "df_info = pd.DataFrame()\n", + "df_info[\"var_name\"] = list(ds.variables)\n", + "df_info[\"dims\"] = df_info.var_name.apply(lambda x: ds[x].dims)\n", + "df_info[\"is_dim\"] = df_info.var_name.apply(lambda x: x in ds.dims)\n", + "df_info[\"n_dim\"] = df_info.var_name.apply(lambda x: len(ds[x].dims))\n", + "df_info[\"attrs\"] = df_info.var_name.apply(lambda x: ds[x].attrs)\n", + "df_info[\"dtype\"] = df_info.var_name.apply(lambda x: ds[x].data.dtype)\n", + "df_info" + ] + }, + { + "cell_type": "markdown", + "id": "e6a36b96", + "metadata": {}, + "source": [ + "### Data cleaning/Preprocessing (skipped)\n", + "Data cleaning and preprocessing is an important stage in the the data analysis pipeline. However it will be out of the scope of this notebook. For more information about data cleaning and preprocessing basics, here are some references. \n", + "* [Xarray Fundamentals](https://earth-env-data-science.github.io/lectures/xarray/xarray.html)\n", + "* [Xarray Tutorial — Pangeo Gallery documentation](http://gallery.pangeo.io/repos/pangeo-data/pangeo-tutorial-gallery/xarray.html)\n", + "* [Pythonic Data Cleaning With pandas and NumPy](https://realpython.com/python-data-cleaning-numpy-pandas/)\n", + "* [Pandas - Cleaning Data](https://www.w3schools.com/python/pandas/pandas_cleaning.asp)" + ] + }, + { + "cell_type": "markdown", + "id": "ae6e1a69", + "metadata": {}, + "source": [ + "### Plotting\n", + "Data visualization (or plotting) is another important data analysis topic and deserves its own discussion. Here in this notebook we will only demonstrate basic tool for simple data visualization tasks.\n", + "\n", + "Here are some reference you might find useful:\n", + "* xarray plotting: https://docs.xarray.dev/en/stable/user-guide/plotting.html\n", + "* Atmospheric Community Toolkit (ACT): https://arm-doe.github.io/ACT/index.html\n", + "\n", + "Note: this notebook is auto-generated using a template. It uses a general idea to select variable(s) to plot and is not customized to each indivdual notebook. Feel free to change the variables in intrrests, especially certain figure is failed to plot." + ] + }, + { + "cell_type": "markdown", + "id": "7e417fb2", + "metadata": {}, + "source": [ + "#### 1-dimenssional basic time series plot\n", + "\n", + "For the following plot we would like to find variables such that\n", + "* it has one and only one dimession\n", + "* \"time\" is its coordinate variable\n", + "* it is not a dimenssion itself,\n", + "* it is not a special variable with substrings within [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "\n", + "Please see the following example in action. (For more details about pandas filtering, please see the following references.)\n", + "* [pandas: multiple conditions while indexing data frame](https://stackoverflow.com/questions/22591174/pandas-multiple-conditions-while-indexing-data-frame-unexpected-behavior)\n", + "* [pandas.DataFrame.apply](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.apply.html)\n", + "* [How to test if a string contains one of the substrings in a list...](https://stackoverflow.com/questions/26577516/how-to-test-if-a-string-contains-one-of-the-substrings-in-a-list-in-pandas" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30edac2d", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==1, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter = df_info[(df_info.n_dim == 1) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4777a659", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_1d = df_filter.var_name.values[0]\n", + " var_1d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb5ae985", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " ds[var_1d].plot()\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "9e41066e", + "metadata": {}, + "source": [ + "#### 2-dimenssional basic plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09bd4adc", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==2, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_2 = df_info[(df_info.n_dim == 2) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2cdea1a9", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_2d = df_filter_2.var_name.values[0]\n", + " var_2d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5f48879", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " print(ds[var_2d].dims)\n", + " # ds[var_2d].plot()\n", + "\n", + " # conventionally, use \"time\" as x-axis\n", + " ds[var_2d].plot(x=\"time\")\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "7554b2a6", + "metadata": {}, + "source": [ + "#### qc-plotting (optional)\n", + "\n", + "Note: act qc plotting has more strict requirement, one of them is the associated qc variable need to have \"flag_masks\" attributes. Which is added by using ds = act.io.armfiles.read_netcdf(files_list), then ds.clean.cleanup()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71c2096f", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " ds_act = act.io.armfiles.read_netcdf(full_path)\n", + " print(type(ds_act))\n", + " ds_act.clean.cleanup()\n", + "\n", + " # or \n", + " # ds.clean.cleanup()\n", + "except Exception as e:\n", + " print(\"ERROR\", e)\n", + " ds_act = ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "844f8505", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter valid variables for ACT qc plotting\n", + "condition = (\"qc_\" + df_info.var_name).apply(lambda x: ds_act[x].attrs.get(\"flag_masks\") is not None \n", + " if x in list(ds.variables) else False)\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_3 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings))) &\n", + " condition\n", + " ]\n", + "df_filter_3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd38cc6", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "try:\n", + " qc_variable = df_filter_3.var_name.values[0]\n", + " print(qc_variable)\n", + "except Exception as e:\n", + " print(e)\n", + " \n", + "try:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + " qc_ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "d76e4d27", + "metadata": {}, + "source": [ + "#### bonus: choose variables to plot from a dropdown menu " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb733804", + "metadata": {}, + "outputs": [], + "source": [ + "# Valid variables filtering\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_4 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82d5c20d", + "metadata": {}, + "outputs": [], + "source": [ + "# example 1: using xarray plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2fc9aaa4", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# fig, ax = plt.subplots(figsize=(10, 4))\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "# fig.clear() # Remove old lines from plot and plot new one\n", + "# if len(ds[var].dims)==2:\n", + "# ds[var].plot(x=\"time\", add_colorbar=False)\n", + "# else:\n", + "# ds[var].plot()\n", + "# plt.grid()\n", + "# plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fabdd802", + "metadata": {}, + "outputs": [], + "source": [ + "# example 2: using act plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "962b4186", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "\n", + "\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "\n", + "# i_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + "# i_display.add_subplots((1,), figsize=(10, 4))\n", + "# ax = i_display.plot(var, subplot_index=(0,), set_title=f\"{var} ({ds_act[var].attrs['long_name']})\",)\n", + "\n", + "# ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "# ax.grid()\n", + "# plt.show()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/BBHRP/.ipynb_checkpoints/bbhrpavg1mlawer.c1-checkpoint.ipynb b/VAPs/quicklook/BBHRP/.ipynb_checkpoints/bbhrpavg1mlawer.c1-checkpoint.ipynb new file mode 100644 index 00000000..3ad7672a --- /dev/null +++ b/VAPs/quicklook/BBHRP/.ipynb_checkpoints/bbhrpavg1mlawer.c1-checkpoint.ipynb @@ -0,0 +1,3768 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# BBHRPAVG1MLAWER.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/bbhrp) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'bbhrpavg1mlawer'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2006-02-27', 'facility': 'C1', 'site': 'sgp', 'start_date': '2000-03-01'}]" + ] + }, + { + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "ac6764f5", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The following locations and date ranges are available for this VAP:\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
sitefacilitystart_dateend_date
0sgpC12000-03-012006-02-27
\n", + "
" + ], + "text/plain": [ + " site facility start_date end_date\n", + "0 sgp C1 2000-03-01 2006-02-27" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2006-02-25'\n", + "date_end = '2006-02-27'" + ] + }, + { + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'/data/archive/sgp/sgpbbhrpavg1mlawerC1.c1'" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['20060225', '20060226', '20060227']" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "51feea2e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['/data/archive/sgp/sgpbbhrpavg1mlawerC1.c1/sgpbbhrpavg1mlawerC1.c1.20060225.002000.cdf',\n", + " '/data/archive/sgp/sgpbbhrpavg1mlawerC1.c1/sgpbbhrpavg1mlawerC1.c1.20060227.002000.cdf']" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2 files loaded\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
<xarray.Dataset>\n",
+       "Dimensions:                               (time: 96, levels: 55, layers: 54)\n",
+       "Coordinates:\n",
+       "  * time                                  (time) datetime64[ns] 2006-02-25T00...\n",
+       "Dimensions without coordinates: levels, layers\n",
+       "Data variables: (12/52)\n",
+       "    base_time                             (time) datetime64[ns] 2006-02-25T00...\n",
+       "    time_offset                           (time) datetime64[ns] 2006-02-25T00...\n",
+       "    height                                (time, levels) float32 dask.array<chunksize=(48, 55), meta=np.ndarray>\n",
+       "    pressure                              (time, levels) float32 dask.array<chunksize=(48, 55), meta=np.ndarray>\n",
+       "    temperature                           (time, levels) float32 dask.array<chunksize=(48, 55), meta=np.ndarray>\n",
+       "    column_ozone                          (time) float32 dask.array<chunksize=(48,), meta=np.ndarray>\n",
+       "    ...                                    ...\n",
+       "    cloud_tot_lwp                         (time) float32 dask.array<chunksize=(48,), meta=np.ndarray>\n",
+       "    cloud_tot_iwp                         (time) float32 dask.array<chunksize=(48,), meta=np.ndarray>\n",
+       "    cloud_fraction                        (time) float32 dask.array<chunksize=(48,), meta=np.ndarray>\n",
+       "    lat                                   (time) float32 36.61 36.61 ... 36.61\n",
+       "    lon                                   (time) float32 -97.49 ... -97.49\n",
+       "    alt                                   (time) float32 315.0 315.0 ... 315.0\n",
+       "Attributes:\n",
+       "    Date:                 Thu Jun  4 22:13:53 2009\n",
+       "    Version:              Version: ver1.5\n",
+       "    missing_value:        -9999.0\n",
+       "    _file_dates:          ['20060225', '20060227']\n",
+       "    _file_times:          ['002000', '002000']\n",
+       "    datastream:           sgpbbhrpavg1mlawerC1.c1\n",
+       "    _datastream:          sgpbbhrpavg1mlawerC1.c1\n",
+       "    _arm_standards_flag:  1
" + ], + "text/plain": [ + "\n", + "Dimensions: (time: 96, levels: 55, layers: 54)\n", + "Coordinates:\n", + " * time (time) datetime64[ns] 2006-02-25T00...\n", + "Dimensions without coordinates: levels, layers\n", + "Data variables: (12/52)\n", + " base_time (time) datetime64[ns] 2006-02-25T00...\n", + " time_offset (time) datetime64[ns] 2006-02-25T00...\n", + " height (time, levels) float32 dask.array\n", + " pressure (time, levels) float32 dask.array\n", + " temperature (time, levels) float32 dask.array\n", + " column_ozone (time) float32 dask.array\n", + " ... ...\n", + " cloud_tot_lwp (time) float32 dask.array\n", + " cloud_tot_iwp (time) float32 dask.array\n", + " cloud_fraction (time) float32 dask.array\n", + " lat (time) float32 36.61 36.61 ... 36.61\n", + " lon (time) float32 -97.49 ... -97.49\n", + " alt (time) float32 315.0 315.0 ... 315.0\n", + "Attributes:\n", + " Date: Thu Jun 4 22:13:53 2009\n", + " Version: Version: ver1.5\n", + " missing_value: -9999.0\n", + " _file_dates: ['20060225', '20060227']\n", + " _file_times: ['002000', '002000']\n", + " datastream: sgpbbhrpavg1mlawerC1.c1\n", + " _datastream: sgpbbhrpavg1mlawerC1.c1\n", + " _arm_standards_flag: 1" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['pressure', 'temperature', 'column_ozone']" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "901cca0c06a4463aa7bea7658c637e66", + "version_major": 2, + "version_minor": 0 + }, + "image/png": "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NXm/7du361e/+pUOPvhgBQIBdezYUWeddZY++eSTJvPOmjVL3bt3V+vWrTVgwAC9/fbb+9wWkvTvf/9b55xzjtq0aaOcnBwdc8wxmjdvXsQ8VvGRpKVLl8rn82np0qWSpMGDB+tvf/ubNmzYELHdpB8um545c6buuOMOHXTQQcrJyVHfvn2bbKPLL79c3bp1a9LWvT8/Pp9P33//vebNmxde1uDBg22t95729Xmqr69Xx44ddemllzZ57fbt25Wbm6uJEyeGp1VWVuqGG25Q9+7dlZ2drQMPPFATJkzgCgoA8Ag9tgCQIv7zn/9Ikjp06BCeVldXpx//+McaN26cbr75ZjU0NCgUCumcc87RG2+8oRtvvFEDBw7Uhg0bNGXKFA0ePFjvvvuucnNztX79eg0fPlwnnXSSHnroIR1wwAH68ssv9fLLL6uurk55eXl64oknNH78eP3iF7/Q3XffLb/fr//85z9as2bNfq/H+++/r48//li//vWv1b17d7Vq1UoVFRXq16+f/H6/Jk+erEMOOURvvfWWpk+frvXr12vu3LnNvufatWt11llnacKECWrVqpU++eQT3XXXXXrnnXf06quvSpJuu+02ff/993rmmWf01ltvhV/bqVOnqO/76KOP6rLLLtM555yjefPmKSsrS3/84x91+umna9GiReEfEjZt2qR27drpzjvvVIcOHbRt2zbNmzdP/fv318qVK3XYYYdJkqqqqjRo0CCtX79eN910k/r3768dO3bo9ddf1+bNm9WrV6/wsu+55x716tUrfAnwbbfdprPOOkvr1q1TYWFh1DZ/+umnGjhwoDp27Kj//d//Vbt27fToo4/q8ssv11dffaUbb7yx+QDt5d5779XPfvYz/fe//9Xzzz9vOc+cOXPUtWtXzZ49W6FQSDNnztSZZ56pZcuWacCAAY6W99Zbb+mUU07RkCFDdNttt0mSCgoKHL2Hnc9TVlaWLrnkEt1///265557Ipbx+OOPq6amRmPHjpW060qI0tJSffHFF5o0aZL69Omj1atXa/LkyVq1apVeeeUVyx93AAAuMgCApDJ37lwjybz99tumvr7eVFVVmZdeesl06NDB5Ofnm4qKCmOMMWPGjDGSzEMPPRTx+scff9xIMs8++2zE9BUrVhhJ5t577zXGGPPMM88YSeaDDz6I2pZrr73WHHDAAc22d8qUKcbqcNO4HuvWrQtP69q1q8nIyDCffvppxLzjxo0zrVu3Nhs2bIiYfvfddxtJZvXq1c22YU+hUMjU19ebZcuWGUnmww8/DD93zTXXWLbVyvfff2/atm1rRowYETE9GAyao48+2vTr1y/qaxsaGkxdXZ3p0aOHuf7668PTp02bZiSZJUuWRH3tunXrjCRz1FFHmYaGhvD0d955x0gyjz/+eLPtvuiii0wgEDCff/55xPQzzzzT5OXlme3btxtjrONjjDGvvfaakWRee+218LThw4ebrl27Rm1rSUmJ2blzZ3h6ZWWladu2rTnttNPC08aMGWP5Hlafn1atWpkxY8Y0u557kmSmTJkS/tvu5+mjjz4ykswDDzwQMV+/fv3McccdF/57xowZxu/3mxUrVkTM1/gdWrhwYXha165dHbUdAGAPlyIDQJI64YQTlJWVpfz8fJ199tkqLi7W3//+dxUVFUXM95Of/CTi75deekkHHHCARowYoYaGhvDjmGOOUXFxcfgS02OOOUbZ2dn62c9+pnnz5umzzz5r0oZ+/fpp+/btGjVqlP76179q69atMa9Xnz591LNnzyZtHjJkiEpKSiLafOaZZ0qSli1b1ux7fvbZZxo9erSKi4uVkZGhrKwslZaWSpI+/vjj/Wrn8uXLtW3bNo0ZMyaiTaFQSGeccYZWrFgRvgy1oaFB5eXl6t27t7Kzs5WZmans7GytXbs2Yvl///vf1bNnT5122mn7XP7w4cOVkZER/rtPnz6SfrgUPZpXX31Vp556qrp06RIx/fLLL1d1dXVEb7VbRo4cqZycnPDf+fn5GjFihF5//XUFg0HXl7cvdj9PRx11lI477riIKwI+/vhjvfPOO7riiisi3u/II4/UMcccE/F+p59+esRl2wCA+OFSZABIUo888ogOP/xwZWZmqqioyPKS2by8vCaXaX711Vfavn27srOzLd+3MTk95JBD9Morr2jmzJm65ppr9P333+vggw/WL3/5S1133XWSpEsvvVQNDQ3605/+pJ/85CcKhUI6/vjjNX36dA0dOnS/1stqPb766iu9+OKLysrKarbNVnbs2KGTTjpJOTk5mj59unr27Km8vDxt3LhRI0eO1M6dO/ernV999ZUk6fzzz486z7Zt29SqVStNnDhR99xzj2666SaVlpaqTZs28vv9uuqqqyKW//XXX+uggw6ytfx27dpF/B0IBCRpn+vzzTffWG7jkpKS8PNuKy4utpxWV1enHTt2NHvpdDw4+TxdccUVuuaaa/TJJ5+oV69emjt3rgKBgEaNGhXxfv/5z3/26/MJAHAHiS0AJKnDDz88XBU5Gqtxfe3bt1e7du308ssvW74mPz8//P+TTjpJJ510koLBoN5991394Q9/0IQJE1RUVKSLLrpIkjR27FiNHTtW33//vV5//XVNmTJFZ599tv7v//5PXbt2DffU1dbWhpMvKfrJfrQ29+nTR3fccYflaxqTMiuvvvqqNm3apKVLl4Z7aaVdBYBi0b59e0nSH/7wh3Dhrr019p43jsUtLy+PeH7r1q064IADwn936NBBX3zxRUzt2pd27dpp8+bNTaZv2rRJ0g/rtWfc9rQ/SVpFRYXltOzsbLVu3Tq8vL2Xtb/L2xcnn6dRo0Zp4sSJevjhh3XHHXfoL3/5i84991y1adMm4v1yc3P10EMPRV0eACC+SGwBIM2cffbZeuKJJxQMBtW/f39br8nIyFD//v3Vq1cvPfbYY3r//ffDiW2jVq1a6cwzz1RdXZ3OPfdcrV69Wl27dg1Xuv3oo490/PHHh+d/8cUXHbV54cKFOuSQQyISCjsaE+U9k2pJ+uMf/9hk3j17Pfd1i6ETTzxRBxxwgNasWaNrr712n23Ye/l/+9vf9OWXX+rQQw8NTzvzzDM1efJkvfrqqzrllFOafc/9deqpp+r555/Xpk2bIhK4Rx55RHl5eeEkfc+4NRa3kqQXXnihyXsGAoFme4qfe+45/eY3vwkny1VVVXrxxRd10kknhS+n7tatm7Zs2aKvvvoq/INAXV2dFi1a5Hh5++Lk89SmTRude+65euSRRzRgwABVVFREXIbc+H7l5eVq166dunfvvt/tAgDsPxJbAEgzF110kR577DGdddZZuu6669SvXz9lZWXpiy++0GuvvaZzzjlH5513nu6//369+uqrGj58uA466CDV1NSEe6Qax4D+9Kc/VW5urk488UR16tRJFRUVmjFjhgoLC8NJ7FlnnaW2bdvqyiuv1LRp05SZmamHH35YGzdutN3madOmacmSJRo4cKB++ctf6rDDDlNNTY3Wr1+vhQsX6v7771fnzp0tXztw4EC1adNGP//5zzVlyhRlZWXpscce04cffthk3qOOOkqSdNddd+nMM89URkaG+vTpY3nZduvWrfWHP/xBY8aM0bZt23T++eerY8eO+vrrr/Xhhx/q66+/1n333SdpV+Lz8MMPq1evXurTp4/ee+89/eY3v2nS5gkTJujJJ5/UOeeco5tvvln9+vXTzp07tWzZMp199tkaMmSI7W0WzZQpU8JjTCdPnqy2bdvqscce09/+9jfNnDkzfFnw8ccfr8MOO0w33HCDGhoa1KZNGz3//PN68803Lbfbc889p/vuu0/HHXec/H5/xNUEGRkZGjp0qCZOnKhQKKS77rpLlZWV4Vs5SdKFF16oyZMn66KLLtL//M//qKamRv/7v/9rOQb3qKOO0tKlS/Xiiy+qU6dOys/Pj0i+98Xp5+mKK67Qk08+qWuvvVadO3duMgZ6woQJevbZZ3XyySfr+uuvV58+fRQKhfT5559r8eLF+tWvfmX7RyQAwH5KdPUqAIAzjdVq967AurcxY8aYVq1aWT5XX19v7r77bnP00UebnJwc07p1a9OrVy8zbtw4s3btWmOMMW+99ZY577zzTNeuXU0gEDDt2rUzpaWl5oUXXgi/z7x588yQIUNMUVGRyc7ONiUlJeaCCy4wH330UcTy3nnnHTNw4EDTqlUrc+CBB5opU6aYP//5z5ZVkYcPH27Z5q+//tr88pe/NN27dzdZWVmmbdu25rjjjjO33nqr2bFjR7PbYvny5WbAgAEmLy/PdOjQwVx11VXm/fffN5LM3Llzw/PV1taaq666ynTo0MH4fD7LqsB7W7ZsmRk+fLhp27atycrKMgceeKAZPny4efrpp8PzfPvtt+bKK680HTt2NHl5eWbQoEHmjTfeMKWlpaa0tDTi/b799ltz3XXXmYMOOshkZWWZjh07muHDh5tPPvnEGPNDpeHf/OY3Tdqivar/RrNq1SozYsQIU1hYaLKzs83RRx8dsR0a/d///Z8ZNmyYKSgoMB06dDC/+MUvzN/+9rcmVZG3bdtmzj//fHPAAQeEt9uebb3rrrvM1KlTTefOnU12drY59thjzaJFi5osb+HCheaYY44xubm55uCDDzZz5syxrIr8wQcfmBNPPNHk5eUZSU22oZ3t4uTzFAwGTZcuXYwkc+utt1ouY8eOHebXv/61Oeyww0x2drYpLCw0Rx11lLn++uvDlcqNoSoyAMSLzxhjEpJRAwCAlLZ+/Xp1795dv/nNb3TDDTckujkAgBTG7X4AAAAAAEmNxBYAAAAAkNS4FBkAAAAAkNTosQUAAAAAJDUSWwAAAABAUiOxBQAAAAAktcxENwDxEQqFtGnTJuXn58vn8yW6OQAAAEBKM8aoqqpKJSUl8vub7z+sqalRXV1ds/NkZ2crJyfHzSamNBLbFLVp0yZ16dIl0c0AAAAA0srGjRvVuXPnqM/X1NSoe9fWqtgSbPZ9iouLtW7dOpJbm0hsU1R+fr4kaZDOUqayEtwauC0zN0tXPDhSD135nBp21ie6OYgjYp1eiHd6Id7pg1inhwbV600tDJ+HR1NXV6eKLUGte6+rCvKte3Yrq0LqftwG1dXVkdjaRGKbohovP85UljJ9JLapJsuXpby8PGX5siSuNE9pxDq9EO/0QrzTB7FOE7tvomp3GGCr1rseVoLckNUxElsAAAAA8FhIRiFZZ7DRpiM6ElsAAAAA8FhIIYWaeQ7OkNgCAAAAgMeCxihorHtmo01HdCS2AAAAAOAxLkV2F4ktAAAAAHgsJKMgia1rSGwBAAAAwGP02LrL+sZJAAAAAAAkCXpsAQAAAMBjFI9yF4ktAAAAAHgstPsR7Tk4Q2ILAAAAAB4LNlM8Ktp0REdiCwAAAAAeC5pdj2jPwRkSWwAAAADwGJciu4vEFgAAAAA8FpJPQfmiPgdnSGwBAAAAwGMhs+sR7Tk4Q2ILAAAAAB4LNtNjG206ovMnugHppqysTD6fL+JRXFwcft4Yo7KyMpWUlCg3N1eDBw/W6tWrE9hiAAAAAG5rTGyjPeAMiW0CHHHEEdq8eXP4sWrVqvBzM2fO1KxZszRnzhytWLFCxcXFGjp0qKqqqhLYYgAAAABuChlfsw84w6XICZCZmRnRS9vIGKPZs2fr1ltv1ciRIyVJ8+bNU1FRkebPn69x48Z53VQAAAAAccClyO4isU2AtWvXqqSkRIFAQP3791d5ebkOPvhgrVu3ThUVFRo2bFh43kAgoNLSUi1fvrzZxLa2tla1tbXhvysrKyVJmblZyvJlxW9lkBBZuZkR/yJ1Eev0QrzTC/FOH8Q6TRhJO+3PHpRfwSgX0AbdaVFa8RljqLnlob///e+qrq5Wz5499dVXX2n69On65JNPtHr1an366ac68cQT9eWXX6qkpCT8mp/97GfasGGDFi1aFPV9y8rKNHXq1CbT58+fr7y8vLisCwAAAIBdqqurNXr0aH333XcqKCiIOl9lZaUKCwv1j1UHqVW+dWL7fVVIpx71+T7fCz/gZyOPnXnmmeH/H3XUURowYIAOOeQQzZs3TyeccIIkyeeLvPTAGNNk2t5uueUWTZw4Mfx3ZWWlunTpooeufI4e2xSUlZupKx78iR668lnV72xIdHMQR8Q6vRDv9EK80wexTg/1pt7R/FyK7C4S2wRr1aqVjjrqKK1du1bnnnuuJKmiokKdOnUKz7NlyxYVFRU1+z6BQECBQKDJ9Iad9eJ7kbrqdzaofqeznSiSE7FOL8Q7vRDv9EGsU1uD08TW+BU0US5F5ppax6iKnGC1tbX6+OOP1alTJ3Xv3l3FxcVasmRJ+Pm6ujotW7ZMAwcOTGArAQAAALgpJJ9C8kd50DPlFD22Hrvhhhs0YsQIHXTQQdqyZYumT5+uyspKjRkzRj6fTxMmTFB5ebl69OihHj16qLy8XHl5eRo9enSimw4AAAAALRKJrce++OILjRo1Slu3blWHDh10wgkn6O2331bXrl0lSTfeeKN27typ8ePH69tvv1X//v21ePFi5efnJ7jlAAAAANzCGFt3kdh67Iknnmj2eZ/Pp7KyMpWVlXnTIAAAAACea36MLYNsnSKxBQAAAACP7Rpja90zyxhb50hsAQAAAMBjIfkVjFLLNyR6bJ0isQUAAAAAj3EpsrtIbAEAAADAY4239rF+jsTWKRJbAAAAAPBY0PgUNFGqIkeZjuhIbAEAAADAY8FmxtgG6bF1jMQWAAAAADwWMn6FooyxDTHG1jESWwAAAADwGD227iKxBQAAAACPhRR9LG3I26akBBJbAAAAAPBY81WRracjOhJbAAAAAPBY8/exJbF1isQWAAAAADwWkk8hRbsUmdv9OEViCwAAAAAeo8fWXSS2AAAAAOCx5qsik9g6xRYDAAAAACQ1emwBAAAAwGMh41Mo2u1+okxHdCS2AAAAAOCxUDOXInO7H+dIbAEAAADAYyHjVyhKkaho0xEdiS0AAAAAeCwon4JRbusTbTqiI7EFAAAAAI/RY+suthgAAAAAeCyoH3ptmz7sC4VCGjNmjE466SSdfPLJ+u9//6snn3xSAwYM0CmnnKKNGzdKktasWaNBgwZpwIABeuWVV+KyTolEjy0AAAAAeMytHtsPPvhAtbW1euONN7RkyRLNmTNHy5cv1xtvvKEVK1bo9ttv1wMPPKBJkyZp7ty5Kioq0hlnnKHTTjvNrVVpEeixBQAAAACPBY2/2YckVVZWRjxqa2ubvE/nzp0lScYYbd++XR06dNARRxyh7OxsnXjiiVq1apUkafPmzerRo4cKCgrUrl07bd261buV9QCJLQAAAAB4zMinUJSH2V08qkuXLiosLAw/ZsyY0eR92rdvL7/fr8MPP1w33nijBg8erIKCgvDzweCuC5uNMeFphYWF2rZtW5zX0FtcigwAAAAAHtuzZ9bqOUnauHFjRJIaCASazLto0SLl5ubqk08+0fvvv6+77rpLrVq1Cj+fkZEhSfL7f1jW9u3b1bZtW1fWo6UgsQUAAAAAj4WMTyFjfVufxukFBQURiW00bdq0kSQdcMAB2rp1qzZs2KC6ujqtWLFCffr0kSQVFxdr7dq1Kioq0rZt29S+fXuX1qRlILEFAAAAAI8F5VcwysjQaNOtDBs2TH/5y19UWlqq2tpazZo1S59//rlKS0uVk5OjRx55RJJUXl6usWPHKhgMatq0aa6sQ0tCYgsAAAAAHrPTY2tHRkaG5s+fHzFt4MCBuuiiiyKm9e7dW2+++abzhiYJElsAAAAA8FhIfoWi9MxGm47oSGwBAAAAwGNB41MwSs9stOmIjsQWAAAAADzm1qXI2IU+bgAAAABAUqPHFgAAAAA8ZoxfoSj3sTVRpiM6ElsAAAAA8FhQPgUVZYxtlOmIjsQWAAAAADwWMtHH0oaMx41JASS2AAAAAOCxUDOXIkebjuhIbAEAAADAYyH5FIpyyXG06YiOxBYAAAAAPMZ9bN1FH3eCzZgxQz6fTxMmTAhPM8aorKxMJSUlys3N1eDBg7V69erENRIAAACAqxovRY72gDNssQRasWKFHnjgAfXp0ydi+syZMzVr1izNmTNHK1asUHFxsYYOHaqqqqoEtRQAAACAm0LyKWSiPLgU2TES2wTZsWOHLr74Yv3pT39SmzZtwtONMZo9e7ZuvfVWjRw5UkceeaTmzZun6upqzZ8/P4EtBgAAAOAWs3uMrdXDkNg6RmKbINdcc42GDx+u0047LWL6unXrVFFRoWHDhoWnBQIBlZaWavny5V43EwAAAEAcRO2t3f2AMxSPSoAnnnhC77//vlasWNHkuYqKCklSUVFRxPSioiJt2LAh6nvW1taqtrY2/HdlZaUkKTMvW1m+LDeajRYkK3fXVzcrL0vyseNLZcQ6vRDv9EK80wexThPGJ1Xbn53b/biLxNZjGzdu1HXXXafFixcrJycn6ny+vXZ6xpgm0/Y0Y8YMTZ06tcn0K/58nvLy8va/wWjRrvjzyEQ3AR4h1umFeKcX4p0+iHVqq66u1iujn7Y9f3M9s/TYOkdi67H33ntPW7Zs0XHHHReeFgwG9frrr2vOnDn69NNPJe3que3UqVN4ni1btjTpxd3TLbfcookTJ4b/rqysVJcuXfTQVc/TY5uCsnIzdcWfR+qhq55T/c6GRDcHcUSs0wvxTi/EO30Q6/RQb+odzc99bN1FYuuxU089VatWrYqYNnbsWPXq1Us33XSTDj74YBUXF2vJkiU69thjJUl1dXVatmyZ7rrrrqjvGwgEFAgEmkxv2NnAJS8prH5nAwfINEGs0wvxTi/EO30Q69TWYJzFlh5bd5HYeiw/P19HHnlkxLRWrVqpXbt24ekTJkxQeXm5evTooR49eqi8vFx5eXkaPXp0IpoMAAAAwGUktu4isW2BbrzxRu3cuVPjx4/Xt99+q/79+2vx4sXKz89PdNMAAAAAuIDE1l0kti3A0qVLI/72+XwqKytTWVlZQtoDAAAAIL5IbN1FHWkAAAAAQFKjxxYAAAAAPGYUvfqx8bYpKYHEFgAAAAA8xqXI7iKxTXE+v08+bveTcnx+X/jfxv8jNRHr9EK80wvxTh/EOj34jE8K2p+fxNZdJLYAAAAA4DESW3eR2AIAAACAx0hs3UViCwAAAAAeM8YnEyWBjTYd0ZHYAgAAAIDHQvJFrYocbTqiI7FNdT7/rgdSS2NMiW/qI9bphXinF+KdPoh1mnAWWy5FdheJLQAAAAB4jEuR3UViCwAAAAAeo8fWXSS2AAAAAOAxemzdRWILAAAAAB4zzfTYktg6R2ILAAAAAB4zkoyJ/hycIbEFAAAAAI+F5JOP2/24hsQWAAAAADzGGFt3kdgCAAAAgMdCxicfVZFdwx2iAQAAAABJjR7bFOfz++Tz8YtPqvH5feF/G/+P1ESs0wvxTi/EO30Q6/QQrfc1GmOaKR5F9SjHSGwBAAAAwGOMsXUXiS0AAAAAeIzE1l0ktgAAAADgMYpHuYvEFgAAAAA8xhhbd5HYproMv+TLSHQr4LaMjB/+zWDPl9KIdXoh3umFeKcPYp0eTNDZ7Cb6Jcckts6R2AIAAACAxxhj6y4SWwAAAADwmNn9iPYcnPEnugEAAAAAkG4ae2yjPZxYunSpTj31VJWWluqvf/2rnnzySQ0YMECnnHKKNm7cKElas2aNBg0apAEDBuiVV16JxyolFD22AAAAAOA1l7psa2pq9Nvf/lZ///vflZ2drfr6eg0aNEhvvPGGVqxYodtvv10PPPCAJk2apLlz56qoqEhnnHGGTjvtNDfWosUgsU11Pt+uB1JLY0yJb+oj1umFeKcX4p0+iHWacBjb5npmd0+vrKyMmBwIBBQIBCKmLV++XLm5uRoxYoTy8vL0P//zPzriiCOUnZ2tE088UTfccIMkafPmzerRo4ckqV27dtq6davat2/vrM0tGJciAwAAAIDHGm/3E+0hSV26dFFhYWH4MWPGjCbv89VXX2ndunV68cUX9bOf/UxlZWUqKCgIPx8MBncv74du4MLCQm3bti2+K+gxemwBAAAAwGN2qiJv3LgxIkndu7dWkg444AANGjRI2dnZOuWUU3TZZZepc+fO4eczdt9uyu//oU9z+/btatu2rSvr0VLQYwsAAAAAXjO+5h+SCgoKIh5WiW2/fv20Zs0aSdLKlSs1bNgwrVmzRnV1dfrnP/+pPn36SJKKi4u1du1aVVZWatu2bSl1GbJEjy0AAAAAeG7PS46tnrOrXbt2+vGPf6yTTz5Zfr9fDz30kN555x2VlpYqJydHjzzyiCSpvLxcY8eOVTAY1LRp01xYg5aFxDbF+TIy5PNlJLoZcJnPnxH+15fBnc5SGbFOL8Q7vRDv9EGs04PPODzndvFGttdcc42uueaa8N8HH3ywLrroooh5evfurTfffNPZGycRLkUGAAAAACQ1emwBAAAAwGN2ikfBPhJbAAAAAEgErkx3DYktAAAAAHiMHlt3kdimuowMieJRqWf3/ciUkSFRhCK1Eev0QrzTC/FOH8Q6PSSweBQoHuW5++67T3369Anfi2rAgAH6+9//Hn7eGKOysjKVlJQoNzdXgwcP1urVqxPYYgAAAADu8+3jASdIbD3WuXNn3XnnnXr33Xf17rvv6pRTTtE555wTTl5nzpypWbNmac6cOVqxYoWKi4s1dOhQVVVVJbjlAAAAAFxj9vGAIyS2DuzcuVPV1dXhvzds2KDZs2dr8eLFtt9jxIgROuuss9SzZ0/17NlTd9xxh1q3bq23335bxhjNnj1bt956q0aOHKkjjzxS8+bNU3V1tebPnx+PVQIAAACQCCS2rmKMrQPnnHOORo4cqZ///Ofavn27+vfvr6ysLG3dulWzZs3S1Vdf7ej9gsGgnn76aX3//fcaMGCA1q1bp4qKCg0bNiw8TyAQUGlpqZYvX65x48ZFfa/a2lrV1taG/66srJQkZQYyleUnzKkmKycj4l+kLmKdXoh3eiHe6YNYp4lQyNn8xrfrEe05OELG48D777+v3/3ud5KkZ555RkVFRVq5cqWeffZZTZ482XZiu2rVKg0YMEA1NTVq3bq1nn/+efXu3VvLly+XJBUVFUXMX1RUpA0bNjT7njNmzNDUqVObTB/zu9OUl5dnq11IPmN+NzTRTYBHiHV6Id7phXinD2Kd2qqrq7Vo9EO25zdm1yPac3CGxNaB6upq5efnS5IWL16skSNHyu/364QTTthn4rmnww47TB988IG2b9+uZ599VmPGjNGyZcvCz/t8kb/QGGOaTNvbLbfcookTJ4b/rqysVJcuXfTIja8py59tu21N7GO5SSWF9hBZORm6bOYpeuTGV1VfE0x0c7xl9ZlModjuLa1jnYaId3oh3ntwcr6RhPv8H2L9muprExDrlrbNHHZsRvBqIOV+nAPXh+qcvYCqyK4isXXg0EMP1YIFC3Teeedp0aJFuv766yVJW7ZsUUFBge33yc7O1qGHHipJ6tu3r1asWKHf//73uummmyRJFRUV6tSpU3j+LVu2NOnF3VsgEFAgEGgyvaEmKPlj2IGS2LZo9TXB9DsZSrPEtlFaxjqNEe/0QryV8olto/raBMW6pW2zUAzt8Xt0brof58ANIYex5VJkV1E8yoHJkyfrhhtuULdu3dS/f38NGDBA0q7e22OPPXa/39cYo9raWnXv3l3FxcVasmRJ+Lm6ujotW7ZMAwcOjLn9AAAAAFoGn2n+ES87duzQnDlzNGTIELVr1045OTk69NBDddVVV2nFihXxW3Cc0WPrwPnnn69BgwZp8+bNOvroo8PTTz31VJ133nm23mPSpEk688wz1aVLF1VVVemJJ57Q0qVL9fLLL8vn82nChAkqLy9Xjx491KNHD5WXlysvL0+jR4+O12o1z5jU6bX1+VreL5ZwzuozSWwBILk5Od9gn+9cS9tmft/+99qGjDe9tl6cAyfgUuRXX31VEyZM0BlnnKEpU6aoV69eys3NVUVFhZYvX65bbrlFhYWFevbZZ+PTgDgisXWouLhYxcXFEdP69etn+/VfffWVLr30Um3evFmFhYXq06ePXn75ZQ0duquYwI033qidO3dq/Pjx+vbbb9W/f38tXrw4PLbXsawsyZ+1f69NNC8S6pa0k3cia3dVxawsKZgCF16kyo8n8ZC9O9bZ2ZLTS5yQfIh3eolHvBN5XEuGfXms22d/1zE7iY/b8fhMxfKesX7O4vk5TYKqyMXFxfrXv/6l3NzciOmFhYU67LDDNHbsWP3rX/+Ky7LjjcR2H0aOHGl73ueee26f8zz44IPNPu/z+VRWVqaysjLbywUAAACQZBLQY9u7d+99ztO/f//4LDzOSGz3obCwMNFNAAAAAJBqElgV+YorrpCx6DmfO3dufBccRyS2+5DMwU16XoxtaGljTtJVKo3lBoBESuRxLRn25bFun2RYR7fF4zMVy3vGGoN0jKGFvn37hv9fU1OjBQsW6Igjjkhgi2JHYutQQ0ODli5dqv/+978aPXq08vPztWnTJhUUFKh169aJbl5TOdmSv+ltgLAfWlJp+sDusTqBgKQEjMOz2hZeld9PN3uOwTOMuUx5xDu9EO/0kcyxjuX8xwljMT7VF4fxyPE8X3G6rRLYYzt+/PiIv3/5y1/qjDPOiO9C44zE1oENGzbojDPO0Oeff67a2loNHTpU+fn5mjlzpmpqanT//fcnuokAAAAAkkELuo9tfX29/vvf/3q6TLeR2Dpw3XXXqW/fvvrwww/Vrl278PTzzjtPV111VQJbBgAAACCZNHe/2njex1aSTjnllPAY22AwqE8//VSjRo2K70LjjMTWgTfffFP//Oc/lZ2dHTG9a9eu+vLLLxPUKgAAAABJJ4GXIt9www3h/9fU1GjhwoU699xz47vQOCOxdSAUCikYbDou4osvvtj/+8wieSTDzcS9YrUtUm0dAQBAbOc/Tvj8TcfZmpD742w5X5EknXXWWRF/jxw5UqWlpVq2bFmCWhQ7ElsHhg4dqtmzZ+uBBx6QtOueszt27NCUKVOafDhaCpOTLZORve8ZEcnh/bX3Swz7aRPY9WKTmyXjz3CnPV6ssxTTeicNF7dlONaBLBm5FGu0WMQ7vSRNvNNhvx1ncTlux0MczgV8LldUNk4qGnv82TVBZ+vqUzOXIsfeHEc2bdqkjRs3erxUd5HYOvC73/1OQ4YMUe/evVVTU6PRo0dr7dq1at++vR5//PFENw8AAABAskhg8aiDDz44Yoztt99+q2nTpsV1mfFGYutASUmJPvjgAz3++ON6//33FQqFdOWVV+riiy9Wbm5uopsHAAAAIFkkcIztSy+9FP5/ZmamOnfurLy8vPguNM5IbB2orq5WXl6errjiCl1xxRWJbg4AAACAZJXAxLZ3797xXUACkNg60LFjR5177rm69NJLNXToUPn9DDpJWX7Ff8xpSC1r3JIX6yy1vPWOB6+2JQB4IR3229glDscv4/O5Os7WZ4z9cbYt/LObyNv97HkpspV169bFtwFxQGLrwCOPPKLHH39c5513ngoKCnThhRfqkksu0fHHH5/opkVlAlkyGVkJWrgHFfTixe2kxOWdqsnaXYQiK1Nm7wqCXrBaZAs+cCQzk91YXCZTxke2nOqId3pJmng7KdbT0tk9N3F5ncOxzs6SScQvn7Gck7nQ3L2XHkuiGzWptXseEsfPswk63FgJ7LG98sortWHDBo0dO1aS9OCDD6pjx4665JJL4rvgOCKxdWDkyJEaOXKkqqqq9Mwzz+jxxx/XwIED1b17d11yySWaPHlyopsIAAAAIBkkMLFdtGiRXn/99fDfAwYMUGlpqcrLy+O74Diij2U/5Ofna+zYsVq8eLE+/PBDtWrVSlOnTk10swAAAAAkicZLkaM94qmiokLbtm0L//3NN99o8+bN8V1onNFjux9qamr0wgsvaP78+Xr55ZfVsWNH3XDDDYluFgAAAIBkkcDb/VxzzTU67rjjdO6550qSnn/+eV133XVxXWa8kdg6sHjxYj322GNasGCBMjIydP7552vRokUqLS1NdNNaJp8vecfZul08oYUXL3DMavuk2joCAHYxJnXG2do9N0mldZZiOydrYQWlohaPsnse0pJim8BLka+77jqdeOKJ+sc//iFjjJ5++ukWXTfIDhJbB84991wNHz5c8+bN0/Dhw5WVlaCiTA6EsjMUyrQZ5iTNQb0Sl0IH+ym0uwhFKJClUAIKjlhtC7fXEbskOtbwVjjeOVkK+Yl3qkvqeLe0fb5XP6Tv53pHjXUiOwBiWHTMVY6Nsbd4B9vb9nlIHD+6oYYGR/MnsiqyJPXt21d9+/aN/4I8QmLrQEVFhQoKChLdDAAAAADJLgE9tn/+858VDAZ16aWXKi8vr8nzK1euVHl5uZ5++un4NCCOSGwdKCgo0H//+1/NnTtX//3vf/X73/9eHTt21Msvv6wuXbroiCOOSHQTAQAAAMDSxRdfrDvvvFO9e/dW7969dfjhhysnJ0cVFRV66623VFRUpBkzZiS6mfuFEXEOLFu2TEcddZT+9a9/6bnnntOOHTskSR999JGmTJmS4NYBAAAASBrNVUSOU49tbm6upk6dqk8++UTXXnut2rdvr4yMDPXv318vvviiXnvtNZ1wwgnxWXic0WPrwM0336zp06dr4sSJys/PD08fMmSIfv/73yewZS7xiXG2zYhLoYMkZbUtUm0dAQDNaEkFeCTvCla6vd6JLLQZw3lfLOdEu5btfhEv2+chRnEdZ+tIAotH5eTk6KyzztJZZ50V3wV5iMTWgVWrVmn+/PlNpnfo0EHffPNNAlq0b6FAhkKZGXFfTqolNJY7a6sdTAyrHcs2y2gsQpGbqWCG84IjMRd9gGfM7libnAyF/Kn1PUNTZs/vdmaSFROCYxlZCYy31eJS7Tq+WDapy9siHOucDAUzErAvj2FbODpnsDur3fe0OleKenccm9s1jp/zYIPD1CqBiW0qSrVdWFwdcMABljcuXrlypQ488MAEtAgAAABAMop2GXJz1ZIRHYmtA6NHj9ZNN92kiooK+Xw+hUIh/fOf/9QNN9ygyy67LNHNAwAAAIC0RGLrwB133KGDDjpIBx54oHbs2KHevXvrpJNO0sCBA/XrX/860c0DAAAAkCzMPh5whDG2DmRlZemxxx7T7bffrvfff1+hUEjHHnusevTokeimJVyqFQ6yLIpgVWQhhgIEidxmMRd9AAAkN7+ajrsMKbW6PKzW0S62RZijcwa7BaliKR4V5dzL9nlVC4ptc5cccymycyS2+zBx4sRmn3/77bfD/581a1a8m+NYfatMmazIMPNFsSEUQ/EomwV+TAw5rX93EYqGvAw1ZDl/o5g/A1bbJxkKG1m1u4ULxzonQw2JKDgCT4XjnZuhhkzineqSJt4xFUp0rxmNYjqGuVwI0q6I43Z9AmJtNy+1mi/WY6fbh95om8/iPCQen7/mNNTvR8HW5Ds1abFIbPdh5cqVtubzpVBvJQAAAIA4oyqyq0hs9+G1115LdBMAAAAApBguRXZXC7nCHAAAAADSiMvFox5//HF16NBBkvTkk09qwIABOuWUU7Rx40ZJ0po1azRo0CANGDBAr7zyiiur0JKQ2KYhr8cbJCWr8aJW2y2GsSiJ/CUu5s+A1fZJhvGryTAOGABamhh27/E41sV0DLN7LE81NreZ5baN9djp9qE3WrwszkNaeq+nm/exDYVCeuaZZ9SlSxfV19dr1qxZWrZsmW6//XbdfvvtkqRJkyZp7ty5WrRokSZPnhyHNUosLkVOcfWt/DJZifn9wre/lQi95GCnYXcHY/uAG0vxqN1FRhpyfGrIdCm+8dj5p2se6eK29O2OdX2eX/UN6bpB00c43q18qm/gt+dUF5d4p+u+vKUnMI2xzvWpPkHnZXuzfZ4W47a1PH+yXWXZQUEoi+kmlk29H5/7hnqHC3RxjO38+fN1/vnn67e//a3Wrl2rI444QtnZ2TrxxBN1ww03SJI2b94cvptLu3bttHXrVrVv397ZglqwlvHNAgAAAIB0YuNS5MrKyohHbW1tk7cJBoN66qmndOGFF0qStm/froKCgojnJcns8YNCYWGhtm3bFo+1ShgSWwAAAADwmJ1Lkbt06aLCwsLwY8aMGU3e59FHH9UFF1wgv39XatemTRtVVlaGn8/I2HUbosbnpV3Jb9u2beO4dt7jUmQAAAAA8JqNS5E3btwY0fsaCASazLpmzRqtXLlSjz76qNauXasHHnhAa9asUV1dnVasWKE+ffpIkoqLi7V27VoVFRVp27ZtKXUZskRiizgy/iQYZ+uT7TEMxmdvnK3P2Bxna9Syxi052Ba2tbR19Eo8tiUA2JGu+3L2u47ZPk+Lcdtanj/5fPbG2RrTZJxt1PMsi8+pLxTDONsW8rkvKCiISGyt3HXXXeH/9+3bV7/73e/0xBNPqLS0VDk5OXrkkUckSeXl5Ro7dqyCwaCmTZsW13YnAoltigtl+RTMtvetbOmV42IWh0JRloux2twu7xiDu4tQBAM+BTPcfe8Iqf6ZSAKexRotQjjeWT4FW8AJFeLLq3i7XvwwSbh+LI9BONbZPgXjPBAwpvM5i9fGfH4YNYHdv41solVptioe5XYF7X0IOX2Ri8WjGr377ruSpIsuukgXXXRRxHO9e/fWm2++uX9vnAQYY+uxGTNm6Pjjj1d+fr46duyoc889V59++mnEPMYYlZWVqaSkRLm5uRo8eLBWr16doBYDAAAAcJubt/sBia3nli1bpmuuuUZvv/22lixZooaGBg0bNkzff/99eJ6ZM2dq1qxZmjNnjlasWKHi4mINHTpUVVVVCWw5AAAAANfYqIoM+7gU2WMvv/xyxN9z585Vx44d9d577+nkk0+WMUazZ8/WrbfeqpEjR0qS5s2bp6KiIs2fP1/jxo1LRLMBAAAAuKi5nll6bJ0jsU2w7777TpLC5bbXrVuniooKDRs2LDxPIBBQaWmpli9fHjWxra2tjbivVWOJ76xMnzIz7V/vn/JfohQZZ5u9O6bZDmK731L9M9HCeRprJBzxTi9exptxts65uS28/m63rHG2NgtFOdBSx9k2hBI/xjadkdgmkDFGEydO1KBBg3TkkUdKkioqKiRJRUVFEfMWFRVpw4YNUd9rxowZmjp1apPp153VTXl5eS62Gi3JL4d3T3QT4JFfnk2s08l1ZxHvdEK80wexTm3V1dUa/RcHLyCxdRWJbQJde+21+uijjyyrk/n2Km1ujGkybU+33HKLJk6cGP67srJSXbp00e+WrFdmdo57jfZSAr/Qlr9MWk2L5ZfBGH5BzM706fqh3fW7JetU17AfG8rmS5z8Qptqv/J7wc72zc70acLp3TV70X7GGkmlMd6zlqxXXZB4p7rsDJ8mDu3marx9MfSMmWbOM5KR3W3hxXrHI9ZO2P5cxKUqsr33tHseEfX2PZbnZDHEdj9e2lBX43gR0RaTWt9Gb5DYJsgvfvELvfDCC3r99dfVuXPn8PTi4mJJu3puO3XqFJ6+ZcuWJr24ewoEApY3bK5vMAr5k/TkiMR2n+oaDIltEnOyffc71khKdUHinU7cjHdsia0rTWgx7Ce2cW7IHhL13U7fxNbee9p9v30JOo0tPbauoiqyx4wxuvbaa/Xcc8/p1VdfVffukZekdO/eXcXFxVqyZEl4Wl1dnZYtW6aBAwd63VwAAAAAccDtftxFj63HrrnmGs2fP19//etflZ+fHx5TW1hYqNzcXPl8Pk2YMEHl5eXq0aOHevToofLycuXl5Wn06NEJbr3HfErYr1XGZ7FDsWqPUZNf9HzG5i+EFq/1jM1ta7kdor2l3fVGmJPtCwB2GJ9vv3ttfcak1OXIdrdFqq23FdufC4vzg5iPVTbf0+55hC8UpdfW8pwshth6cZ5Gj62rSGw9dt9990mSBg8eHDF97ty5uvzyyyVJN954o3bu3Knx48fr22+/Vf/+/bV48WLl5+c7Xl4w2ydlezC+IAm+fI52yjFcimx3m8WSBIYydr04lOVTcD+uu7C9LZxss9Q+J0iY4O5YB7N9CmYkuDGIu8Z4mywpxDVVKc/s/k67Gm8j7fcOOcb9eCJ/3Ix+XNu/Rrm9LqHdsQ5ly9V9ubPj+b5XyvZQrBjP+2Kq0u1gUKrXn8lQaD9elATn0MmCxNZjxs4vhz6fysrKVFZWFv8GAQAAAPAc97F1F4ktAAAAAHiNS5FdRWILAAAAAB6jx9ZdjOBJcfEo0W69oBiX4wFH4yzsjumIYdxJIndYcbklETtgAEi8WI7HHo2bjAe3x1ImS1Lh9vHc9phWj8Zj2x7zG2V6i4+j2ccDjtBjm+IaAlJG09vb2ubVDsGLwf2JLB7l9vo1Fp4IBqRg0J33dHTwSIIfMix5dZBwcfuEC464GGu0XKE4fLfRcjnal8ew/4qpUE8Si+UcJm7FozwoDOd6gcgW9oNH1NjY3K7x/Jw73W3TY+suemwBAAAAAEmNHlsAAAAA8BrFo1xFYgsAAAAAXiOxdRWXIqNZXo238WIcQSKLRyXDOAlHN0FPgvWx5NX4sWTdPgBarhj2XzEV6klisZzDJPO2cL1ApEeFouyKGptQjK9PgMYxttEecIYe2xQXzJaUHTmtJX5RkqF4lNXrHSWDdhZr87XBeBShaIGfC0suf1bi8dlz8zsWLi6TTTGhdEC800vM8Y6hCr/bx6+YX+/VXRwSxMvvttvFoxwVl7QrlrtuRPmc2f1Mx7V4lNPtQo+tq0hsAQAAAMBjPmPkM9YZbLTpiI7EFgAAAAC8Ro+tq0hsAQAAAMBj3MfWXRSPSkMt8QbsyVA8yur1bo87SehOrAV+Liy5vI3isc1b4ncMQAqyua/x4vgV8+sTOb43xbhdPCqh47EdfE7tfqZbVMJo9vGAI/TYprhQtuTL3vd8ktw/KDj5QiZo2VF3bnZ3pBY/DXmR1DQWjApmS0GbVQD3h5Odf9Imc7EcXD0Q9CjWaBnC8Q4Q73TgKN4e/Gga890D4iFBPxa7fUyL9bsdczLmdqGoGNsT02cySrdcLAm4W/EOOnwfemzdRWILAAAAAF5jjK2rSGwBAAAAwGP02LqLxBYAAAAAvEaPrasoHoUfuP0FcjLOIEHLjjqmwu44DYtxMqn0C5uTMSdJu96x3CQeALwSwz7I7r7c0X7cq32+B+ttudgWdkyLeQyo24WiYmxPTJ/JKGOUYymIlsh4N/ba7v2Ac/TYprhgtqRAolvRcsV8EPe4KEGjeBSYoTpwnO3ntgjHOodiQumgMd6hLClEvFNeKB7x9qoycRIUj2pJP1yGY53twXfb7WTOo0JRlot2kFS7XQ16fziOrTG7HtGegyMktgAAAADgMcbYuovEFgAAAAC8xhhbVzHGFgAAAACQ1EhskdZivhl9EhQlsCse42GTYb09w7YA4IVY9uUpVjzK9mtTbf/sdv0PjwpFWS7aQUEor8YMu8kXav4BZ7gUOcWFcoyU04K+wdF4dSDee7FRL/+waJDVvH57C49b8agco2DInfjGmoRSKCo+figU5l6s0XJFfLcpHJLygrv3m/sd71hO5FtioSi73E5gPFi/cKwDCfpuWyzSOmm0ef7jgO3k1GrRVrGJcu7lpNCULfvx2pDTkykuRXYViS0AAAAAeIziUe4isQUAAAAAr3G7H1eR2AIAAACAx+ixdRfFo9AyeHVD+b0XG+21VnsTq3lD9haeDDunWMfIJsM6AkBKiaX4T0ssFGWX20WPWtr6xYPd8at2z38csD321e444CjnXk4KTdnixefC7OMBR+ixTXGhQHIUj3K9Yp7tBTf3nvt+Y68qCu6tcZ8eChiF4llwJFYtreBIEoo51kgqoUQXmIGnYi0oFFMVWLuJTpJwvUCRy2IuFBaF25WALc9/Yt1ALndeGJuFO6O93q79We2QwyKP9Ni6i8QWAAAAALzGGFtXkdgCAAAAgMfosXUXiS0AAAAAeI372LqK4lEpLlnGy8Tyq1RM6xjltXbf0+2xLZ7x6nPR0tYbAFJITHUe7BbqSRKuFyhKEm7X+rBdUMqJWM45rOJls3BntNfb5cXnorHHNtoDztBjm+JMTlCh3GCimxF3tr77FnvrqDsNY/M9YylCEcMey/h2/SZlckMKmdD+vIGt2aI10Yt1xC4xxxpJpTHeyg3JEO/U50K8m+xlLYsoWey041D9NqHsHreteLHecfxu7+9nIFrBrabv52v6ptE2tt0K3DYLe1lOi1I8Ki5JeZP3a/5pI4fn3CGz6xHtOThCYgsAAAAAXuNSZFeR2AIAAACAx3xqpniUpy1JDYyxRfqw2HNEvaQ2hpu62x93m8Bdls1GRmtiUqwjAKQju5djWu2fk7mHyOVxnEnN5mfA9rhkJ5etx3KJsd1LlqOMsbW+tNrl8xC3PyuNt/uJ9rDpvffe00knnaTS0lJdcMEFqq+v15NPPqkBAwbolFNO0caNGyVJa9as0aBBgzRgwAC98sorLq9M4tFjm+Iychrkz23Y79cbjxITXyzjTWNpY7QxpDYLE/isxnn44v8rm293w32BoPxyaayOwxA0WUe7BbfSddztfn4o/Lt/f/TnNijDrVijxQrHO4d4pwNH8ba567Q8JtpMNhztn1tYIhnLuYAXx6VwrAPx/27b/QzY/qzEelYTy+a1iI3luZdkef5lO7Yunbj5jLNzbrdu93PggQdq0aJFysvL06RJk7RgwQLNmjVLb7zxhlasWKHbb79dDzzwgCZNmqS5c+eqqKhIZ5xxhk477TRH7W3p6LFNgNdff10jRoxQSUmJfD6fFixYEPG8MUZlZWUqKSlRbm6uBg8erNWrVyemsQAAAABarOLiYuXl5UmSsrKy9H//93864ogjlJ2drRNPPFGrVq2SJG3evFk9evRQQUGB2rVrp61btyay2a4jsU2A77//XkcffbTmzJlj+fzMmTM1a9YszZkzRytWrFBxcbGGDh2qqqoqj1sKAAAAIC7MPh6SKisrIx61tbVR3+7zzz/XK6+8okGDBqmgoCA8PRjcVa3Z7HF5c2FhobZt2+byCiUWiW0CnHnmmZo+fbpGjhzZ5DljjGbPnq1bb71VI0eO1JFHHql58+apurpa8+fPT0BrAQAAALjNZ0yzD0nq0qWLCgsLw48ZM2ZYvldlZaUuvfRSzZ07Vx07dlRlZWX4uYyMDEmS3/9D6rd9+3a1bds2jmvnPcbYtjDr1q1TRUWFhg0bFp4WCARUWlqq5cuXa9y4cZ62x+cznoyzNca332NcYmqjxa3ZpF3jN+yMszUhX9OxHkbJWcouyrawzeZ6xxLrpJasnwsALYPNfbTlMdHqtRb7JEf751j2abEeb6zeMoZzgVQ7Ltn9DNj+rMR6AIsl3sbXZLCp5bmXFNtnOlHH6NDuR7TnJG3cuDGi9zUQCDSZNRgM6uKLL9bkyZPVs2dP1dfXa82aNaqrq9OKFSvUp08fSbsuWV67dq2Kioq0bds2tW/f3uUVSiwS2xamoqJCklRUVBQxvaioSBs2bIj6utra2ohLExp/pcnPDSoj1+HNolOU1QEv6q3DrJJam4U2LIv/uXzADOy+2KIwr0G1LhWhcLJ9vFhH7BLYHYWCnKBrsUbL1Rjvwlz3vttoucL78v2Mt+V+2+a+3G91/EqS/bjdBNZqPrvrmMjjdqwdCnY/A7EUmXJQsNeS3dj4rM69ohSPsvpMW4nn5zwYcnbOvWfPrNVzklRQUBCR2Fp56qmntHz5clVVVen222/X1VdfrQkTJqi0tFQ5OTl65JFHJEnl5eUaO3asgsGgpk2b5qityYDEtoXy7fVNNsY0mbanGTNmaOrUqU2mT/IfqTx/nuvtS3kZiW6APZP8Rya6CfAIsU4vt/iOSnQT4KH9jne6XgWSxOttK9ZJvH7prtpfrdFOXrDHWFrL52waNWqURo0a1WT6RRddFPF379699eabb9p/4yRDYtvCFBcXS9rVc9upU6fw9C1btjTpxd3TLbfcookTJ4b/rqysVJcuXVQe+rcyQjnxa3ASSbUe20n+I1Ue+jc9tikuHrFGy9UY7xlmFfFOAwH5dYvvqP2ONz22zudLZI+t3VjTY7vntKavbdk9tjXOXtDc/Wpj3chpiMS2henevbuKi4u1ZMkSHXvssZKkuro6LVu2THfddVfU1wUCActr7msV4l6IuxmLdCz6j2Q272Nr8Q7W9yGPz86pViH3ElsH28fLdcQubsYaLR/xTi/7G2/r/bbNxNby+JUc+3G7x2ir+eyuYyKP23bXz8nrLRPbGLZjrFvHbmycnG9YfaatxPNzHnT4PXbrPrbYhcQ2AXbs2KH//Oc/4b/XrVunDz74QG3bttVBBx2kCRMmqLy8XD169FCPHj1UXl6uvLw8jR7t6OIG7MWqSEK0egaWxaNsFiWwqj+QDIUpnGyfZF1HAEgllvttm/vykPE16eFKlv243UJRVvPZXcdEbotYC3fa/QzEUmTK54utQ9FubIxp2msbrXiU1WfaSov6nNNj6yoS2wR49913NWTIkPDfjZcQjxkzRg8//LBuvPFG7dy5U+PHj9e3336r/v37a/HixcrPz3e8rPycOmXk7v/O0e5lHaEYL5uxuxy77LYn2j4jaOzdCSvD1/SXuWaGQu+T3e2QbTKkWqkgt1Z1vuYLFcS6LazYXUe345qOnMQayY94p5dskyHVNI13LElN0EZFf8l6P+5kn+12YhDr5bdWxzqr41qs672/Gr/b+Tl1CuzHd9vJeZbVeoesel0t3tPudozH5dJWnym/RQKb4bfuFbW8CsGDc7I9NZjo95i14gvtekR7Ds6Q2CbA4MGDI26QvDefz6eysjKVlZV51ygAAAAA3qHH1lUktgAAAADgNZeqImMXElsAAAAA8Jid+9jCPnsDCZG27I7piHV8SqxjdPdmv+S79XSrsbNWrMbixrIfcns7SLFvCyt21zEe6wMAqS6W8asZUW6FsjfLcZiOxnG6u3+Pdcyu9e2Lms4X63onirPxzxavtxx/ajGm1e4td2KMl9XrLcf8WowZD4as0xfrccT70bjG9/Pic9F4KXK0BxyhxzbFtcv7Xll59YluRotgtcOLttNqsNhpWu3sbU9z+XqSrN1FKNrmfq96lwrMONk+sawjBaWcaYx1+7wdrsUaLVdjvNu5+N1Gy5W1u3jU/sbbah9td1+e6aAyTSL323aTC7vr7cUx2ko8jtuS/fWOZZrdgppO2I2NVUdDZrTiUTbPySxf69JnoD5U5+wFRop6hyBOlxwjsQUAAAAAj3EpsrtIbAEAAADAa0bNVEX2tCUpgcQWAAAAALzG7X5cRfEopA2r8RPRxl5Yjd+IacyKxRiYlsbJ9ollHZOhSAcAJAO740Wt5mtwMG4ykfvtWMZIWr02WY/R0dhd71im2S2o6YTd2FiN77WqgxLt9bGM0Ubyocc2xXXIqVJ2brateTNSqKhP0O6OLMqBvS6U0WSa1U7YqviG32Ka29s2M5QpbZeKc6vU4G9w/Hqr7RNtW1hxex1jLdpg94DkRYEQt2WGdu2mi3P2L9ZILsQ7vTiJt939XL3F8cuKk/24F/vOWBMLu8c1L47RVpwct+2ew0j2j91WP2RYJ4IWiWQCi0dZnWdl+q2Lb9k9J4unuqDD4lEhKepH39umpwQSWwAAAADwGMWj3EViCwAAAABeY4ytq0hsAQAAAMBrJLauongUwpyM6Wjp7I6XiTb2Itti/IbVeBDrMStNp7W0bWu1fZyMQ3F7HWMdW2V3/BfFIQAkK7v7uawo4w/35mQ/7sW+M9ZxvHaPa8l6jI7G7rHbevypvW1m9dpY2S0eZXWe1RBlHLndc7IWpTGxjfaAI/TYpriDcr9VIDcr0c1oEawq60Wrlldv7BWPyrAY2e9FRUF/aFdMS3K2K+Svd+U9nWwfuydOyVisKV7sfgb2jkM8Yo2Wi3inl1jjbZVw2i0eZXc/LiV2X243qbZ7DPOq6m+T5cYYa6v1k6JUErbot7KuBt10vnqLisOWPwZEiUuGzc+K1eutXmtVKCrLZ/3ZtZput6q2W2obHMaW4lGuIrEFAAAAAI9RPMpdJLYAAAAA4DXG2LqKxBYAAAAAvBYyUrTLpUMktk618BHVgHusxtBEG3thNU4jlnEs0cbGtCROto/dMVwUa/qB3c+AF2O9AKQGq7GvdsfO2t2PS4ndl9sd32v3GJZKx2gptvoffov5svxWr206LdpY2mhjb+283uq1VoWirOqgRJserVZIi0HxKFfRY5viOmdvU252ZJjT9cTZsrBElB1wTahpwS2rZNdqWjy2b5ODTzBbktQ9e4uUUdfsa62KQ1hxsn0sCzTEUOXASQVIK8lc0XKfdsf64MC+Y40UsDveXbO/Jt7pIMZ4Wxb/iXLSvze39+PxYvcYZne9rRJlT86LwrHeGhHrWJNqq+O01Xta/RBvnQju/2cqVlYJtN1zL0nK9jXYW04ci6HtrLfXhh80l8CS2DpFYgsAAAAAXmOMratIbAEAAADAayGjqD2zjLF1jMQWAAAAALxmQrse0Z6DIy1/tDxiYjVuJBmKJMSDZWGJKL+S5VjcPN1qjInVtHhsX7tjjKzYHTPlZPtYjsuJoY2xjpF1dUyrC1ramF8Aqcmy+E+U8Yd7c3s/Hi92j2F219vumFSvxDq+1+6YYauCUpbjrG2Oc40Hu+N7o435rTP2+utaVGFLike5ih7bFNctc6taZce/GmtLq/hqtz31sl9Zz+rgkW2z+Ibb6x0KBvSNpO7ZX8ufUevKe9bbPCBIUpZFgYZoFRL3Fq3ScizsVj2Mx7LjLRQMqELSIdlbXIs1Wq7GeB9KvNNCKBjQFu1/vK2qyNrdl1vtx6Oxu3+3y27l3Kivtyx2aO+4n6gCWcYf0LeSumd9Ld9+xDra+lltS7vFIK0SQavl1HlUPMpu8h2tSJRlQU+PCzB9n+XwRwAuRXZVy/tpDgAAAAAAB+ixBQAAAACvURXZVSS2AAAAAOA1o2YSW09bkhK4FBlhsRRPsDuG1KsCDXbbkyXrsRBW4zSsx6fYK76RDAW7nIy3shrDZXfMlN3xsE7YHTsbj2UDQKJYjR+0uy93Ulch1jGxe4t13KN1sUN7x/2WWCDLjmjrZ7Ut7RaDtBqrarUcq3oi8RC0iI1VzZNoRaIsC3q2pEJRVige5Sp6bFNc24ydap1hs7BTDAcauzsOrwbx221PfZSEs86iqFS2RRJslVBlx1CYwm9z/xuU9I2k9hm1ysioaXZeu7UHdm0zewUtrOJot+1eF3JIdkHjU4Wktv46ZfgpJpTqGuNt57uN5BeUtEVN423/h8Km0+rkl1S3z9daHavs7sfjwUmdHMuCSS6fhzg5VtlZdlBG30pqk1GzX9/taMuw+sHWuqiYvaTP6of4aIU2Y2G1HKsE2uqHmqwonRd2z9PiKTvD4TlgKCRFO28Mcbsfp0hsAQAAAMBrjLF1FYktAAAAAHiNxNZVJLYAAAAA4DXuY+uq5BxBj7iIZYC93bEoXg3it9seJ+M0rMbdWo1tqYvhaxWPfVg8xr5axdHZWF4AQDR298dW+3e7dR6sjlWJPI92Mr7XsmCSy+chTo5VXtSOiLYMqzGk1kXFmp7X2C08Fa3QZiyslmNVkNOqyJmT+igtvWikMaFmH3CGHtsU1z5Dync05t+LHUBidjJBi0s66mVkNWi/3khSZMGCLItmZ1msS4bPJzvr6I9hOzTs3qm38fuV6XeeSIcsDma7to/NohoW62i1Phk219HJtrBqu12xbPNEaTBZkqR2/ixl+r2pTInEaYx3G3+mMv0colNdw+6TdjvxDlrs+6z2h/XG+ri2tyyb+/FEs3u82rXG+z4+WB0xdx3TrLi3PRokfaZd52WZe5yXWZ2bRGf1GWiqzuI9d81XHzGt3nbhKff7wayWY9XZYPVDTVaUglBW52l2z0Pcku13mIwaE/0XJS5FdoyjJgAAAAB4rblOBRJbx0hsAQAAAMBroZAUZVicuBTZMRJbAAAAAPAaPbauongU0obVGBqrMbKS9TiNeov9S33UsT/7FstY0VhZjoeNOsaoKat1tBwHZXMdnWyLWMaAJXKbA0AsrMYKWu0Ps2zuy+tt7scTze7xyu4JrVUfmLNxru5ycuy1YrXe2Ta3j9VYVevCU+73HFotx2osr1WRM6uxwbumN51m9zwkUUwo1OwDztBjm+KylaGAzd19rDvXvVkdKNxehhNBY5rUgahXSFkW89abUJPkNsvXdDtmWWxbu+voj+F3pQbfrq9uni9TmT6rNWheyOLQbrV9osny2atIZjcJzbDYtk4EW9jlOrGuz57qQ7vim+vLUpbTohRIOj/Ee/++20gujftyO/G2Sjqt9uVWxy8rVse0WI5LsbJal2isjld2k3L7x6XYzlf2PgfK2F2xN6AMZe5dvdfGoqKtX7SiYnv/wGH1Q0ZQpklyW2+krL3eMyhfk6rK0eod2a1ubfV6q9fmWCTfuzolrH7UsXm+G8eCUg1OPzf02LqKHtsW7N5771X37t2Vk5Oj4447Tm+88UaimwQAAADADSHT/AOOkNi2UE8++aQmTJigW2+9VStXrtRJJ52kM888U59//nmimwYAAAAgVsbsKhJl+SCxdYrEtoWaNWuWrrzySl111VU6/PDDNXv2bHXp0kX33XdfopsGAAAAAC0KY2xboLq6Or333nu6+eabI6YPGzZMy5cvt3xNbW2tamtrw39XVlZKkhqCATUE7Y2HbFD8x9nGYxlO7N0en3aNs92bT7vGKe2pQU3HbzSo6ThbJ+u4v4WQGoKBiH/3R/Sb3ttYvuyPZbGzjg2KfWyXk/FZ8ebG+oTfy4VYI3kQ7/TiNN7W42wtjms26g5E24/HUqAvVk6KV9ktYmjF7nHJzXG28TpuS9HH2e7JJ6khyjjbPfkVrQhT021hdaVsUPs/ztbqtd+r6TjboKRMi/bYPTdpUPzG2QaDzs5FTMjIWIwjliRDj61jJLYt0NatWxUMBlVUVBQxvaioSBUVFZavmTFjhqZOndpk+lsfTldeXl5c2onEe/2DaYluAjzy6gdTEt0EeGjpB03350hdxDt9vPXh9EQ3AXFUXV0tabT9F5iQrGt1i/vY7gcS2xbMt9cvhcaYJtMa3XLLLZo4cWL478rKSnXp0kUnHzNZBfn2emxjkchfeFOJ3V6+hmBAr34wRaccM1WZGbX7fkGc24P4aQgG9MoHt+m0Y26Pa6zRMhDv9OIk3i3pqpR4SJb1299bIjUEA3r9g2k6+ZjJcf9uu33bpkTeDike4nnlYGVVcN8z7YEeW3eR2LZA7du3V0ZGRpPe2S1btjTpxW0UCAQUCPxweUvjl2HnzhplZsQ/OSGxdYfd28TUNwRVXV2t6upqZWXWxa09xDXxImNNopPqiHd6cRLvlniPWTe1tNu2RbP/ie0Psc7MiN9xW3L/RwISW/t27ty17e0mpQ2mNmrPbIPqXWtXuiCxbYGys7N13HHHacmSJTrvvPPC05csWaJzzjnH1ntUVVVJknoc90Vc2oiWwMGlLkhyxDq9EO/0QrzTB7FOF1VVVSosLIz6fHZ2toqLi/VmxcJm36e4uFjZ2dluNy9lkdi2UBMnTtSll16qvn37asCAAXrggQf0+eef6+c//7mt15eUlGjjxo3Kz8+PevkyklfjpeYbN25UQUFBopuDOCLW6YV4pxfinT6IdXowxqiqqkolJSXNzpeTk6N169aprq753vvs7Gzl5OS42cSURmLbQl144YX65ptvNG3aNG3evFlHHnmkFi5cqK5du9p6vd/vV+fOnePcSiRaQUEBB8g0QazTC/FOL8Q7fRDr1NdcT+2ecnJySFpdRmLbgo0fP17jx49PdDMAAAAAoEWj5CkAAAAAIKmR2AJJKBAIaMqUKRGVsJGaiHV6Id7phXinD2INxJ/PcJMkAAAAAEASo8cWAAAAAJDUSGwBAAAAAEmNxBYAAAAAkNRIbAEPvf766xoxYoRKSkrk8/m0YMGC8HP19fW66aabdNRRR6lVq1YqKSnRZZddpk2bNu3zfVetWqXS0lLl5ubqwAMP1LRp07T38Plly5bpuOOOU05Ojg4++GDdf//9bq8eLNx7773q3r27cnJydNxxx+mNN94IP2eMUVlZmUpKSpSbm6vBgwdr9erV+3xP4t0y8f1OL3y30wffbSBJGACeWbhwobn11lvNs88+aySZ559/Pvzc9u3bzWmnnWaefPJJ88knn5i33nrL9O/f3xx33HHNvud3331nioqKzEUXXWRWrVplnn32WZOfn2/uvvvu8DyfffaZycvLM9ddd51Zs2aN+dOf/mSysrLMM888E69VhTHmiSeeMFlZWeZPf/qTWbNmjbnuuutMq1atzIYNG4wxxtx5550mPz/fPPvss2bVqlXmwgsvNJ06dTKVlZVR35N4t1x8v9MH3+30wncbSA4ktkCC7H1wtPLOO+8YSeGTJSv33nuvKSwsNDU1NeFpM2bMMCUlJSYUChljjLnxxhtNr169Il43btw4c8IJJ+z/CmCf+vXrZ37+859HTOvVq5e5+eabTSgUMsXFxebOO+8MP1dTU2MKCwvN/fffH/U9iXdy4Pud2vhupy++20DLxaXIQAv23Xffyefz6YADDghPu/zyyzV48ODw32+99ZZKS0sj7o13+umna9OmTVq/fn14nmHDhkW89+mnn653331X9fX18VyFtFVXV6f33nuvyXYfNmyYli9frnXr1qmioiLi+UAgoNLSUi1fvjw8jXinLr7fyYnvNvaF7zaQGCS2QAtVU1Ojm2++WaNHj1ZBQUF4eqdOnXTQQQeF/66oqFBRUVHEaxv/rqioaHaehoYGbd26NV6rkNa2bt2qYDBoud0rKirCsYn2fCPinZr4ficvvttoDt9tIHEyE90AAE3V19froosuUigU0r333hvx3IwZM5rM7/P5Iv42u4tP7Dndzjxwn9V231dc9pxGvFMP3+/UwHcbe+O7DSQWPbZAC1NfX68LLrhA69at05IlSyJ+8bVSXFwc0QsgSVu2bJH0w6+/0ebJzMxUu3btXGw9GrVv314ZGRmW272oqEjFxcWSFPX5aIh3cuP7nfz4bsMK320g8UhsgRak8cC4du1avfLKK7YOXAMGDNDrr7+uurq68LTFixerpKRE3bp1C8+zZMmSiNctXrxYffv2VVZWlqvrgF2ys7N13HHHNdnuS5Ys0cCBA9W9e3cVFxdHPF9XV6dly5Zp4MCBUd+XeCcvvt+pge829sZ3G2ghElGxCkhXVVVVZuXKlWblypVGkpk1a5ZZuXKl2bBhg6mvrzc//vGPTefOnc0HH3xgNm/eHH7U1taG3+Pmm282l156afjv7du3m6KiIjNq1CizatUq89xzz5mCggLLWwZcf/31Zs2aNebBBx/klgEeaLwlyIMPPmjWrFljJkyYYFq1amXWr19vjNl1S5DCwkLz3HPPmVWrVplRo0Y1uSUI8U4efL/TB9/t9MJ3G0gOJLaAh1577TUjqcljzJgxZt26dZbPSTKvvfZa+D3GjBljSktLI973o48+MieddJIJBAKmuLjYlJWVhW8X0Gjp0qXm2GOPNdnZ2aZbt27mvvvu82CNcc8995iuXbua7Oxs86Mf/cgsW7Ys/FwoFDJTpkwxxcXFJhAImJNPPtmsWrUq4vXEO3nw/U4vfLfTB99tIDn4jNk9Ch0AAAAAgCTEGFsAAAAAQFIjsQUAAAAAJDUSWwAAAABAUiOxBQAAAAAkNRJbAAAAAEBSI7EFAAAAACQ1ElsAAAAAQFIjsQUAAAAAJDUSWwAAAABAUiOxBQAAAAAkNRJbAAAAAEBSI7EFAAAAACQ1ElsAAAAAQFIjsQUAj/h8PluPpUuXJrqpCbNw4UKVlZUluhkx2bRpk8rKyvTBBx8kuinNGjx4sI488shENyNszZo1Kisr0/r16z1ZntPP2uWXX65u3brFrT1OlJWVyefzJboZANCikNgCgEfeeuutiMdZZ52l3NzcJtN/9KMfJbqpCbNw4UJNnTo10c2IyaZNmzR16tQWn9i2NGvWrNHUqVM9TWyT/bMGAPhBZqIbAADp4oQTToj4u0OHDvL7/U2mp5Lq6mrl5eUluhktph0AACA+6LEFgBakrq5O06dPV69evRQIBNShQweNHTtWX3/9dcR83bp109lnn62XXnpJxx57rHJzc3X44YfrpZdekiQ9/PDDOvzww9WqVSv169dP7777bsTrL7/8crVu3VqrV6/WqaeeqlatWqlDhw669tprVV1dHTGvMUb33nuvjjnmGOXm5qpNmzY6//zz9dlnn0XM13hp6+uvv66BAwcqLy9PV1xxhSTpySef1LBhw9SpU6dwW2+++WZ9//33EW265557JEVetr1+/XqtX79ePp9PDz/8cJNt5vP5Ii4pbbxM8/3339f555+vNm3a6JBDDnG0Llb+85//aOzYserRo4fy8vJ04IEHasSIEVq1alV4nqVLl+r444+XJI0dOza8Dvu65LWiokLjxo1T586dlZ2dre7du2vq1KlqaGiImG/q1Knq37+/2rZtq4KCAv3oRz/Sgw8+KGNMk/ecP3++BgwYoNatW6t169Y65phj9OCDDzaZb8WKFTrppJOUl5engw8+WHfeeadCodA+t0dNTY1uueUWde/eXdnZ2TrwwAN1zTXXaPv27RHzRVv/bt266fLLL5e06/P6//7f/5MkDRkyJLzdGuPd+Nl64403dMIJJyg3N1cHHnigbrvtNgWDwfB7Ll261PJy/r0/P8191pyw83maMGGCWrVqpcrKyiavv/DCC1VUVKT6+vrwtCeffFIDBgxQq1at1Lp1a51++ulauXKlo3YBQDoisQWAFiIUCumcc87RnXfeqdGjR+tvf/ub7rzzTi1ZskSDBw/Wzp07I+b/8MMPdcstt+imm27Sc889p8LCQo0cOVJTpkzRn//8Z5WXl+uxxx7Td999p7PPPrvJ6+vr63XWWWfp1FNP1YIFC3Tttdfqj3/8oy688MKI+caNG6cJEybotNNO04IFC3Tvvfdq9erVGjhwoL766quIeTdv3qxLLrlEo0eP1sKFCzV+/HhJ0tq1a3XWWWfpwQcf1Msvv6wJEyboqaee0ogRI8Kvve2223T++edLirxsu1OnTvu1PUeOHKlDDz1UTz/9tO6//37H67K3TZs2qV27drrzzjv18ssv65577lFmZqb69++vTz/9VJL0ox/9SHPnzpUk/frXvw6vw1VXXRX1fSsqKtSvXz8tWrRIkydP1t///nddeeWVmjFjhn76059GzLt+/XqNGzdOTz31lJ577jmNHDlSv/jFL3T77bdHzDd58mRdfPHFKikp0cMPP6znn39eY8aM0YYNG5os++KLL9Yll1yiF154QWeeeaZuueUWPfroo81uC2OMzj33XN1999269NJL9be//U0TJ07UvHnzdMopp6i2trbZ1+9t+PDhKi8vlyTdc8894e02fPjwiLZedNFFuvjii/XXv/5V559/vqZPn67rrrvO0bIk9z5rdj5PV1xxhaqrq/XUU09FvHb79u3661//qksuuURZWVmSpPLyco0aNUq9e/fWU089pb/85S+qqqrSSSedpDVr1jheTwBIKwYAkBBjxowxrVq1Cv/9+OOPG0nm2WefjZhvxYoVRpK59957w9O6du1qcnNzzRdffBGe9sEHHxhJplOnTub7778PT1+wYIGRZF544YWIZUsyv//97yOWdccddxhJ5s033zTGGPPWW28ZSea3v/1txHwbN240ubm55sYbbwxPKy0tNZLMP/7xj2bXOxQKmfr6erNs2TIjyXz44Yfh56655hpjdWhat26dkWTmzp3b5DlJZsqUKeG/p0yZYiSZyZMnR8znZF3saGhoMHV1daZHjx7m+uuvD09vjJdVW62MGzfOtG7d2mzYsCFi+t13320kmdWrV1u+LhgMmvr6ejNt2jTTrl07EwqFjDHGfPbZZyYjI8NcfPHFzS63MV7/+te/Iqb37t3bnH766c2+9uWXXzaSzMyZMyOmP/nkk0aSeeCBB8LT9o5Po65du5oxY8aE/3766aeNJPPaa69Fbetf//rXiOk//elPjd/vD2+71157zfI9rD4/0T5r0YwZM8Z07do1/LeTz9OPfvQjM3DgwIj57r33XiPJrFq1yhhjzOeff24yMzPNL37xi4j5qqqqTHFxsbngggvC0xo/4wCAH9BjCwAtxEsvvaQDDjhAI0aMUENDQ/hxzDHHqLi4uMnllcccc4wOPPDA8N+HH364pF2Xbe45nrRx+t69dZJ08cUXR/w9evRoSdJrr70WbpPP59Mll1wS0abi4mIdffTRTdrUpk0bnXLKKU2W89lnn2n06NEqLi5WRkaGsrKyVFpaKkn6+OOP7Wwex37yk59E/O10XfbW0NCg8vJy9e7dW9nZ2crMzFR2drbWrl0b0zq89NJLGjJkiEpKSiLadeaZZ0qSli1bFp731Vdf1WmnnabCwsLwdpw8ebK++eYbbdmyRZK0ZMkSBYNBXXPNNftcdnFxsfr16xcxrU+fPpaflT29+uqrkhS+lLjR//t//0+tWrXSP/7xj30u26n8/Hz9+Mc/jpg2evRohUIhvf76664vb1+cfJ7Gjh2r5cuXh3v2JWnu3Lk6/vjjw5WpFy1apIaGBl122WUR75eTk6PS0tK0rpYOAHZQPAoAWoivvvpK27dvV3Z2tuXzW7dujfi7bdu2EX83vi7a9JqamojpmZmZateuXcS04uJiSdI333wTbpMxRkVFRZZtOvjggyP+trqUc8eOHTrppJOUk5Oj6dOnq2fPnsrLy9PGjRs1cuTIJpdIu2Xvtjhdl71NnDhR99xzj2666SaVlpaqTZs28vv9uuqqq2Jah6+++kovvvhi+HLUvTXG/Z133tGwYcM0ePBg/elPfwqPx12wYIHuuOOOcBsax2N37tx5n8veO/6SFAgE9rk+33zzjTIzM9WhQ4eI6T6fT8XFxeHPj5us4rb359VLTj5PF198sW644QY9/PDDmjFjhtasWaMVK1bo3nvvjXg/SeEx2nvz++mLAIDmkNgCQAvRvn17tWvXTi+//LLl8/n5+a4ur6GhQd98801EclNRUSHph4Snffv28vl8euONNxQIBJq8x97TrO6t+eqrr2rTpk1aunRpuJdWUpMiQ83JycmRpCZjN5tLaPZui9N12dujjz6qyy67LDwWtNHWrVt1wAEHNPva5rRv3159+vTRHXfcYfl8SUmJJOmJJ55QVlaWXnrppfD2kKQFCxZEzN+YbH7xxRfq0qXLfrerOe3atVNDQ4O+/vrriOTWGKOKioqI5CwQCFiOuXWajFqNgd778xrtc7L3j0JucPJ5atOmjc455xw98sgjmj59uubOnaucnByNGjUq4v0k6ZlnnlHXrl1dby8ApDoSWwBoIc4++2w98cQTCgaD6t+/vyfLfOyxx/TLX/4y/Pf8+fMl7bqcubFNd955p7788ktdcMEF+7WMxgRz75P/P/7xj03mbZxn586dys3NDU8vKipSTk6OPvroo4j5//rXv9puR6zr4vP5mqzD3/72N3355Zc69NBDLdfBbrsWLlyoQw45RG3atGl2+ZmZmcrIyAhP27lzp/7yl79EzDds2DBlZGTovvvu04ABA2y1walTTz1VM2fO1KOPPqrrr78+PP3ZZ5/V999/r1NPPTU8rVu3bk3i9uqrr2rHjh0R0/a13aqqqvTCCy9EXI48f/58+f1+nXzyyeFlSdJHH32k008/PTzfCy+80OT9on3W7HL6eRo7dqyeeuopLVy4UI8++qjOO++8iB9ETj/9dGVmZuq///1vk8voAQD7RmILAC3ERRddpMcee0xnnXWWrrvuOvXr109ZWVn64osv9Nprr+mcc87Reeed59rysrOz9dvf/lY7duzQ8ccfr+XLl2v69Ok688wzNWjQIEnSiSeeqJ/97GcaO3as3n33XZ188slq1aqVNm/erDfffFNHHXWUrr766maXM3DgQLVp00Y///nPNWXKFGVlZemxxx7Thx9+2GTeo446SpJ011136cwzz1RGRob69Omj7OxsXXLJJXrooYd0yCGH6Oijj9Y777wTTsTtiHVdzj77bD388MPq1auX+vTpo/fee0+/+c1vmlzye8ghhyg3N1ePPfaYDj/8cLVu3VolJSXhnte9TZs2TUuWLNHAgQP1y1/+Uocddphqamq0fv16LVy4UPfff786d+6s4cOHa9asWRo9erR+9rOf6ZtvvtHdd9/dJNnu1q2bJk2apNtvv107d+7UqFGjVFhYqDVr1mjr1q2aOnWq7W0WzdChQ3X66afrpptuUmVlpU488UR99NFHmjJlio499lhdeuml4XkvvfRS3XbbbZo8ebJKS0u1Zs0azZkzR4WFhRHv2TjW9IEHHlB+fr5ycnLUvXv3cG9su3btdPXVV+vzzz9Xz549tXDhQv3pT3/S1VdfrYMOOkjSrkuTTzvtNM2YMUNt2rRR165d9Y9//EPPPfdck3Vo7rNmh9PP07Bhw9S5c2eNHz9eFRUVGjt2bMT7devWTdOmTdOtt96qzz77TGeccYbatGmjr776Su+8845atWrlSuwAIGUltnYVAKSvvasiG2NMfX29ufvuu83RRx9tcnJyTOvWrU2vXr3MuHHjzNq1a8Pzde3a1QwfPrzJe0oy11xzTcS0xoqwv/nNb5os+6OPPjKD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aQRbAAAAAIClEWwBAAAAAJZGsAUAAAAAWBrBFgAAAABgaQRbAAAAAIClEWwBAAAAAJZGsAUAAAAAWBrBFgAAAABgaQRbAAAAAIClEWwBAAAAAJZGsAUAAAAAWBrBFgAAAABgaQRbAAAAAIClEWwBAAAAAJZGsAUAAAAAWBrBFgAAAABgaQRbAAAAAIClEWwBAAAAAJZGsAUAAAAAWBrBFgAAAABgaQRbAAAAAIClEWwBAAAAAJZGsAUAAAAAWBrBFgAAAABgaQRbAAAAAIClEWwBAAAAAJZGsAUAAAAAWBrBFgAAAABgaQRbAAAAAIClEWwBAAAAAJZGsAUAAAAAWBrBFgAAAABgaQRbAAAAAIClEWwBAAAAAJZGsAUAAAAAWBrBFgAAAABgaQRbAAAAAIClEWwBAAAAAJZGsAUAAAAAWBrBFgAAAABgaQRbAAAAAIClEWwBAAAAAJZGsAUAAAAAWBrBFgAAAABgaQRbAAAAAIClEWwBAAAAAJZGsAUAAAAAWBrBFgAAAABgaQRbAAAAAIClEWwBAAAAAJZGsAUAAAAAWBrBFgAAAABgaQRbAAAAAIClEWwBAAAAAJZGsAUAAAAAWBrBFgAAAABgaQRbAAAAAIClEWwBAAAAAJZGsAUAAAAAWBrBFgAAAABgaQRbAAAAAIClEWwBAAAAAJZGsAUAAAAAWBrBFgAAAABgaQRbAAAAAIClEWwBAAAAAJZGsAUAAAAAWBrBFgAAAABgaQRbAAAAAIClEWwBAAAAAJZGsAUAAAAAWBrBFgAAAABgaQRbAAAAAIClEWwBAAAAAJZGsAUAAAAAWBrBFgAAAABgaQRbAAAAAIClEWwBAAAAAJZGsAUAAAAAWBrBFgAAAABgaQRbAAAAAIClEWwBAAAAAJZGsAUAAAAAWBrBFgAAAABgaQRbAAAAAIClEWwBAAAAAJZGsAUAAAAAWNr/B2OKQCR826BBAAAAAElFTkSuQmCC", + "text/html": [ + "\n", + "
\n", + "
\n", + " Figure\n", + "
\n", + " \n", + "
\n", + " " + ], + "text/plain": [ + "Canvas(toolbar=Toolbar(toolitems=[('Home', 'Reset original view', 'home', 'home'), ('Back', 'Back to previous …" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'flux'" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [ + { + "ename": "KeyError", + "evalue": "'flux'", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mKeyError\u001b[0m Traceback (most recent call last)", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/core/dataset.py:1348\u001b[0m, in \u001b[0;36mDataset._construct_dataarray\u001b[0;34m(self, name)\u001b[0m\n\u001b[1;32m 1347\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m-> 1348\u001b[0m variable \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_variables\u001b[49m\u001b[43m[\u001b[49m\u001b[43mname\u001b[49m\u001b[43m]\u001b[49m\n\u001b[1;32m 1349\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mKeyError\u001b[39;00m:\n", + "\u001b[0;31mKeyError\u001b[0m: 'flux'", + "\nDuring handling of the above exception, another exception occurred:\n", + "\u001b[0;31mKeyError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[0;32mIn[11], line 7\u001b[0m\n\u001b[1;32m 5\u001b[0m qc_display \u001b[38;5;241m=\u001b[39m act\u001b[38;5;241m.\u001b[39mplotting\u001b[38;5;241m.\u001b[39mTimeSeriesDisplay(ds)\n\u001b[1;32m 6\u001b[0m qc_display\u001b[38;5;241m.\u001b[39madd_subplots((\u001b[38;5;241m2\u001b[39m,), figsize \u001b[38;5;241m=\u001b[39m (\u001b[38;5;241m9.5\u001b[39m,\u001b[38;5;241m10\u001b[39m))\n\u001b[0;32m----> 7\u001b[0m qc_ax \u001b[38;5;241m=\u001b[39m \u001b[43mqc_display\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mplot\u001b[49m\u001b[43m(\u001b[49m\u001b[43mqc_variable\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43msubplot_index\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m0\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mset_title\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mQC results on field: \u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m \u001b[49m\u001b[38;5;241;43m+\u001b[39;49m\u001b[43m \u001b[49m\u001b[43mqc_variable\u001b[49m\u001b[43m,\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 8\u001b[0m qc_ax\u001b[38;5;241m.\u001b[39mgrid()\n\u001b[1;32m 9\u001b[0m qc_display\u001b[38;5;241m.\u001b[39mqc_flag_block_plot(qc_variable, subplot_index\u001b[38;5;241m=\u001b[39m(\u001b[38;5;241m1\u001b[39m,))\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/act/plotting/timeseriesdisplay.py:418\u001b[0m, in \u001b[0;36mTimeSeriesDisplay.plot\u001b[0;34m(self, field, dsname, subplot_index, cmap, set_title, add_nan, day_night_background, invert_y_axis, abs_limits, time_rng, y_rng, use_var_for_y, set_shading, assessment_overplot, overplot_marker, overplot_behind, overplot_markersize, assessment_overplot_category, assessment_overplot_category_color, force_line_plot, labels, cbar_label, cbar_h_adjust, secondary_y, y_axis_flag_meanings, colorbar_labels, cb_friendly, **kwargs)\u001b[0m\n\u001b[1;32m 415\u001b[0m assessment_overplot_category_color[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mAcceptable\u001b[39m\u001b[38;5;124m'\u001b[39m] \u001b[38;5;241m=\u001b[39m (\u001b[38;5;241m0.0\u001b[39m, \u001b[38;5;241m0.4240129715562796\u001b[39m, \u001b[38;5;241m0.4240129715562796\u001b[39m),\n\u001b[1;32m 417\u001b[0m \u001b[38;5;66;03m# Get data and dimensions\u001b[39;00m\n\u001b[0;32m--> 418\u001b[0m data \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_obj\u001b[49m\u001b[43m[\u001b[49m\u001b[43mdsname\u001b[49m\u001b[43m]\u001b[49m\u001b[43m[\u001b[49m\u001b[43mfield\u001b[49m\u001b[43m]\u001b[49m\n\u001b[1;32m 419\u001b[0m dim \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mlist\u001b[39m(\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_obj[dsname][field]\u001b[38;5;241m.\u001b[39mdims)\n\u001b[1;32m 420\u001b[0m xdata \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_obj[dsname][dim[\u001b[38;5;241m0\u001b[39m]]\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/core/dataset.py:1439\u001b[0m, in \u001b[0;36mDataset.__getitem__\u001b[0;34m(self, key)\u001b[0m\n\u001b[1;32m 1437\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39misel(\u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkey)\n\u001b[1;32m 1438\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m utils\u001b[38;5;241m.\u001b[39mhashable(key):\n\u001b[0;32m-> 1439\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_construct_dataarray\u001b[49m\u001b[43m(\u001b[49m\u001b[43mkey\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 1440\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m utils\u001b[38;5;241m.\u001b[39miterable_of_hashable(key):\n\u001b[1;32m 1441\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_copy_listed(key)\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/core/dataset.py:1350\u001b[0m, in \u001b[0;36mDataset._construct_dataarray\u001b[0;34m(self, name)\u001b[0m\n\u001b[1;32m 1348\u001b[0m variable \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_variables[name]\n\u001b[1;32m 1349\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mKeyError\u001b[39;00m:\n\u001b[0;32m-> 1350\u001b[0m _, name, variable \u001b[38;5;241m=\u001b[39m \u001b[43m_get_virtual_variable\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_variables\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mname\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mdims\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 1352\u001b[0m needed_dims \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mset\u001b[39m(variable\u001b[38;5;241m.\u001b[39mdims)\n\u001b[1;32m 1354\u001b[0m coords: \u001b[38;5;28mdict\u001b[39m[Hashable, Variable] \u001b[38;5;241m=\u001b[39m {}\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/core/dataset.py:186\u001b[0m, in \u001b[0;36m_get_virtual_variable\u001b[0;34m(variables, key, dim_sizes)\u001b[0m\n\u001b[1;32m 184\u001b[0m split_key \u001b[38;5;241m=\u001b[39m key\u001b[38;5;241m.\u001b[39msplit(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m.\u001b[39m\u001b[38;5;124m\"\u001b[39m, \u001b[38;5;241m1\u001b[39m)\n\u001b[1;32m 185\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mlen\u001b[39m(split_key) \u001b[38;5;241m!=\u001b[39m \u001b[38;5;241m2\u001b[39m:\n\u001b[0;32m--> 186\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mKeyError\u001b[39;00m(key)\n\u001b[1;32m 188\u001b[0m ref_name, var_name \u001b[38;5;241m=\u001b[39m split_key\n\u001b[1;32m 189\u001b[0m ref_var \u001b[38;5;241m=\u001b[39m variables[ref_name]\n", + "\u001b[0;31mKeyError\u001b[0m: 'flux'" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "ae3815a803c64f629cc4a6feb7130f6b", + "version_major": 2, + "version_minor": 0 + }, + "image/png": "iVBORw0KGgoAAAANSUhEUgAAA7YAAAPoCAYAAADqfmIfAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAA9hAAAPYQGoP6dpAABCA0lEQVR4nO3df2zV9b348Veh0Kr3toswKwgy3NXJLhm7lMAot1l0WgOGG5LdwOKNqBeTNdsuAa7egdzoICbN3c3MvU7BLYJmCbrGn/GPXkdzcy8/hJuMpiyLkLtFuBa2VlLMWtTdIvD5/mHo93YtDpCe9oWPR3L+OO99PvR1trfdefL5HE9ZURRFAAAAQFJjRnoAAAAA+CSELQAAAKkJWwAAAFITtgAAAKQmbAEAAEhN2AIAAJCasAUAACA1YQsAAEBqwhYAAIDUhC0AAACpCVsAAABSE7YAAACkJmwBAABITdgCAACQmrAFAAAgNWELAABAasIWAACA1IQtAAAAqQlbAAAAUhO2AAAApCZsAQAASE3YAgAAkJqwBQAAIDVhCwAAQGrCFgAAgNSELQAAAKkJWwAAAFITtgAAAKQmbAEAAEhN2AIAAJCasAUAACA1YQsAAEBqwhYAAIDUhC0AAACpCVsAAABSE7YAAACkJmwBAABITdgCAACQmrAFAAAgNWELAABAasIWAACA1IQtAAAAqQlbAAAAUhO2AAAApCZsAQAASE3YAgAAkJqwBQAAIDVhCwAAQGrCFgAAgNSELQAAAKkJWwAAAFITtgAAAKQmbAEAAEhN2AIAAJCasAUAACA1YQsAAEBqwhYAAIDUhC0AAACpCVsAAABSE7YAAACkJmwBAABITdgCAACQmrAFAAAgNWELAABAasIWAACA1IQtAAAAqQlbAAAAUhO2AAAApCZsAQAASE3YAgAAkJqwBQAAIDVhCwAAQGrCFgAAgNSELQAAAKkJWwAAAFITtgAAAKQmbAEAAEhN2AIAAJCasAUAACA1YQsAAEBqwhYAAIDUhC0AAACpCVsAAABSE7YAAACkJmwBAABITdgCAACQmrAFAAAgNWELAABAasIWAACA1IQtAAAAqQlbAAAAUhO2AAAApCZsAQAASE3YAgAAkJqwBQAAIDVhCwAAQGrCFgAAgNSELQAAAKkJWwAAAFITtgAAAKQmbAEAAEhN2AIAAJCasAUAACA1YQsAAEBqwhYAAIDUhC0AAACpCVsAAABSE7YAAACkJmwBAABITdgCAACQmrAFAAAgNWELAABAasIWAACA1IQtAAAAqQlbAAAAUhO2w2znzp2xePHimDx5cpSVlcWrr776R8/ZsWNH1NbWRmVlZdxwww3x1FNPDf+gAAAASQnbYfb+++/HrFmz4oknnjiv4w8fPhyLFi2K+vr6aG9vj4ceeihWrlwZL7300jBPCgAAkFNZURTFSA/xaVFWVhavvPJKLFmy5JzHfPe7343XXnstDh482L/W2NgYv/jFL2Lv3r0lmBIAACCX8pEegIH27t0bDQ0NA9buuOOO2LJlS3z44Ycxbty4Ic/r6+uLvr6+/udnzpyJd999NyZMmBBlZWXDOjMAAHzaFUURJ06ciMmTJ8eYMW6MLTVhO8p0dXVFTU3NgLWampo4depUdHd3x6RJk4Y8r6mpKTZs2FCKEQEAgHM4cuRITJkyZaTH+NQRtqPQH15hPXu3+MddeV23bl2sWbOm/3lPT09cf/31ceTIkaiqqhqeQQEAgIiI6O3tjalTp8af/umfjvQon0rCdpS59tpro6ura8DasWPHory8PCZMmHDO8yoqKqKiomLQelVVlbAFAIAS8THAkeHm71Fm/vz50draOmBt+/btMWfOnHN+vhYAAODTTNgOs/feey/2798f+/fvj4iPvs5n//790dHREREf3UK8fPny/uMbGxvj7bffjjVr1sTBgwdj69atsWXLlnjggQdGYnwAAIBRz63Iw2zfvn1xyy239D8/+znYe+65J5599tno7Ozsj9yIiOnTp0dLS0usXr06nnzyyZg8eXI8/vjj8fWvf73kswMAAGTge2wvU729vVFdXR09PT0+YwsAAMPM+++R5VZkAAAAUhO2AAAApCZsAQAASE3YAgAAkJqwBQAAIDVhCwAAQGrCFgAAgNSELQAAAKkJWwAAAFITtgAAAKQmbAEAAEhN2AIAAJCasAUAACA1YQsAAEBqwhYAAIDUhC0AAACpCVsAAABSE7YAAACkJmwBAABITdgCAACQmrAFAAAgNWELAABAasIWAACA1IQtAAAAqQlbAAAAUhO2AAAApCZsAQAASE3YAgAAkJqwBQAAIDVhCwAAQGrCFgAAgNSELQAAAKkJWwAAAFITtgAAAKQmbAEAAEhN2AIAAJCasAUAACA1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338/Zs2aFU888cR5He/9d4kVpDR37tyisbFxwNrNN99crF27dsjj/+Ef/qG4+eabB6x985vfLL7yla8M24zkcKF7aShf/OIXiw0bNlzq0UjmYvfSsmXLin/8x38sHnnkkWLWrFnDOCFZXOhe+rd/+7eiurq6OH78eCnGI5kL3U///M//XNxwww0D1h5//PFiypQpwzYj+URE8corr3zsMd5/l5YrtgmdPHky2traoqGhYcB6Q0ND7NmzZ8hz9u7dO+j4O+64I/bt2xcffvjhsM3K6HYxe+kPnTlzJk6cOBFXX331cIxIEhe7l5555pl466234pFHHhnuEUniYvbSa6+9FnPmzInvf//7cd1118VNN90UDzzwQPz+978vxciMYhezn+rq6uLo0aPR0tISRVHEO++8Ey+++GLceeedpRiZy4j336VVPtIDcOG6u7vj9OnTUVNTM2C9pqYmurq6hjynq6tryONPnToV3d3dMWnSpGGbl9HrYvbSH/rBD34Q77//fixdunQ4RiSJi9lLv/71r2Pt2rWxa9euKC/3f0d85GL20qFDh2L37t1RWVkZr7zySnR3d8e3vvWtePfdd33O9lPuYvZTXV1dbNu2LZYtWxb/+7//G6dOnYq/+qu/ih/+8IelGJnLiPffpeWKbWJlZWUDnhdFMWjtjx0/1DqfPhe6l856/vnn43vf+140NzfHNddcM1zjkcj57qXTp0/HXXfdFRs2bIibbrqpVOORyIX8Xjpz5kyUlZXFtm3bYu7cubFo0aJ47LHH4tlnn3XVloi4sP104MCBWLlyZTz88MPR1tYWr7/+ehw+fDgaGxtLMSqXGe+/S8dfkSc0ceLEGDt27KC/aTx27NigvxU669prrx3y+PLy8pgwYcKwzcrodjF76azm5uZYsWJFvPDCC3HbbbcN55gkcKF76cSJE7Fv375ob2+P73znOxHxUZwURRHl5eWxffv2uPXWW0syO6PLxfxemjRpUlx33XVRXV3dvzZjxowoiiKOHj0aN95447DOzOh1MfupqakpFixYEA8++GBERHzpS1+Kq666Kurr6+PRRx91lY3z5v13ablim9D48eOjtrY2WltbB6y3trZGXV3dkOfMnz9/0PHbt2+POXPmxLhx44ZtVka3i9lLER9dqb333nvjueee85kjIuLC91JVVVX88pe/jP379/c/Ghsb4wtf+ELs378/5s2bV6rRGWUu5vfSggUL4re//W289957/Wu/+tWvYsyYMTFlypRhnZfR7WL20wcffBBjxgx8izx27NiI+P9X2+B8eP9dYiP0L63iE/rpT39ajBs3rtiyZUtx4MCBYtWqVcVVV11V/M///E9RFEWxdu3a4u677+4//tChQ8WVV15ZrF69ujhw4ECxZcuWYty4ccWLL744Ui+BUeJC99Jzzz1XlJeXF08++WTR2dnZ//jd7343Ui+BUeJC99If8m9F5qwL3UsnTpwopkyZUvz1X/918eabbxY7duwobrzxxuL+++8fqZfAKHKh++mZZ54pysvLi02bNhVvvfVWsXv37mLOnDnF3LlzR+olMEqcOHGiaG9vL9rb24uIKB577LGivb29ePvtt4ui8P57pAnbxJ588sli2rRpxfjx44vZs2cXO3bs6P/P7rnnnuKrX/3qgOP/8z//s/iLv/iLYvz48cXnPve5YvPmzSWemNHqQvbSV7/61SIiBj3uueee0g/OqHOhv5f+L2HL/3Whe+ngwYPFbbfdVlxxxRXFlClTijVr1hQffPBBiadmtLrQ/fT4448XX/ziF4srrriimDRpUvE3f/M3xdGjR0s8NaPNf/zHf3zseyDvv0dWWVG4pwIAAIC8fMYWAACA1IQtAAAAqQlbAAAAUhO2AAAApCZsAQAASE3YAgAAkJqwBQAAIDVhCwAAQGrCFgAAgNSELQAAAKkJWwAAAFITtgAAAKQmbAEAAEhN2AIAAJCasAUAACA1YQsAAEBqwhYAAIDUhC0AAACpCVsAAABSE7YAAACkJmwBAABITdgCAACQmrAFAAAgNWELAABAasIWAACA1IQtAAAAqQlbAAAAUhO2AAAApCZsAQAASE3YAgAAkJqwBQAAIDVhCwAAQGrCFgAAgNSELQAAAKkJWwAAAFITtgAAAKQmbAEAAEhN2AIAAJCasAUAACA1YQsAAEBqwhYAAIDUhC0AAACpCVsAAABSE7YAAACkJmyH2c6dO2Px4sUxefLkKCsri1dfffWPnrNjx46ora2NysrKuOGGG+Kpp54a/kEBAACSErbD7P33349Zs2bFE088cV7HHz58OBYtWhT19fXR3t4eDz30UKxcuTJeeumlYZ4UAAAgp7KiKIqRHuLToqysLF555ZVYsmTJOY/57ne/G6+99locPHiwf62xsTF+8YtfxN69e0swJQAAQC7lIz0AA+3duzcaGhoGrN1xxx2xZcuW+PDDD2PcuHFDntfX1xd9fX39z8+cORPvvvtuTJgwIcrKyoZ1ZgAA+LQriiJOnDgRkydPjjFj3BhbasJ2lOnq6oqampoBazU1NXHq1Kno7u6OSZMmDXleU1NTbNiwoRQjAgAA53DkyJGYMmXKSI/xqSNsR6E/vMJ69m7xj7vyum7dulizZk3/856enrj++uvjyJEjUVVVNTyDAgAAERHR29sbU6dOjT/90z8d6VE+lYTtKHPttddGV1fXgLVjx45FeXl5TJgw4ZznVVRUREVFxaD1qqoqYQsAACXiY4Ajw83fo8z8+fOjtbV1wNr27dtjzpw55/x8LQAAwKeZsB1m7733Xuzfvz/2798fER99nc/+/fujo6MjIj66hXj58uX9xzc2Nsbbb78da9asiYMHD8bWrVtjy5Yt8cADD4zE+AAAAKOeW5GH2b59++KWW27pf372c7D33HNPPPvss9HZ2dkfuRER06dPj5aWlli9enU8+eSTMXny5Hj88cfj61//eslnBwAAyMD32F6ment7o7q6Onp6enzGFgAAhpn33yPLrcgAAACkJmwBAABITdgCAACQmrAFAAAgNWELAABAasIWAACA1IQtAAAAqQlbAAAAUhO2AAAApCZsAQAASE3YAgAAkJqwBQAAIDVhCwAAQGrCFgAAgNSELQAAAKkJWwAAAFITtgAAAKQmbAEAAEhN2AIAAJCasAUAACA1YQsAAEBqwhYAAIDUhC0AAACpCVsAAABSE7YAAACkJmwBAABITdgCAACQmrAFAAAgNWELAABAasIWAACA1IQtAAAAqQlbAAAAUhO2AAAApCZsAQAASE3YAgAAkJqwBQAAIDVhCwAAQGrCFgAAgNSELQAAAKkJWwAAAFITtgAAAKQmbAEAAEhN2AIAAJCasAUAACA1YQsAAEBqwhYAAIDUhC0AAACpCVsAAABSE7YAAACkJmwBAABITdgCAACQmrAFAAAgNWELAABAasIWAACA1IQtAAAAqQlbAAAAUhO2AAAApCZsAQAASE3YAgAAkJqwLZFNmzbF9OnTo7KyMmpra2PXrl0fe/y2bdti1qxZceWVV8akSZPivvvui+PHj5doWgAAgDyEbQk0NzfHqlWrYv369dHe3h719fWxcOHC6OjoGPL43bt3x/Lly2PFihXx5ptvxgsvvBA///nP4/777y/x5AAAAKOfsC2Bxx57LFasWBH3339/zJgxI/7lX/4lpk6dGps3bx7y+P/6r/+Kz33uc7Fy5cqYPn16/OVf/mV885vfjH379pV4cgAAgNFP2A6zkydPRltbWzQ0NAxYb2hoiD179gx5Tl1dXRw9ejRaWlqiKIp455134sUXX4w777zznD+nr68vent7BzwAAAA+DYTtMOvu7o7Tp09HTU3NgPWampro6uoa8py6urrYtm1bLFu2LMaPHx/XXnttfOYzn4kf/vCH5/w5TU1NUV1d3f+YOnXqJX0dAAAAo5WwLZGysrIBz4uiGLR21oEDB2LlypXx8MMPR1tbW7z++utx+PDhaGxsPOefv27duujp6el/HDly5JLODwAAMFqVj/QAl7uJEyfG2LFjB12dPXbs2KCruGc1NTXFggUL4sEHH4yIiC996Utx1VVXRX19fTz66KMxadKkQedUVFRERUXFpX8BAAAAo5wrtsNs/PjxUVtbG62trQPWW1tbo66ubshzPvjggxgzZuD/NGPHjo2Ij670AgAA8P8J2xJYs2ZNPP3007F169Y4ePBgrF69Ojo6OvpvLV63bl0sX768//jFixfHyy+/HJs3b45Dhw7FG2+8EStXroy5c+fG5MmTR+plAAAAjEpuRS6BZcuWxfHjx2Pjxo3R2dkZM2fOjJaWlpg2bVpERHR2dg74Ttt77703Tpw4EU888UT8/d//fXzmM5+JW2+9Nf7pn/5ppF4CAADAqFVWuLf1stTb2xvV1dXR09MTVVVVIz0OAABc1rz/HlluRQYAACA1YQsAAEBqwhYAAIDUhC0AAACpCVsAAABSE7YAAACkJmwBAABITdgCAACQmrAFAAAgNWELAABAasIWAACA1IQtAAAAqQlbAAAAUhO2AAAApCZsAQAASE3YAgAAkJqwBQAAIDVhCwAAQGrCFgAAgNSELQAAAKkJWwAAAFITtgAAAKQmbAEAAEhN2AIAAJCasAUAACA1YQsAAEBqwhYAAIDUhC0AAACpCVsAAABSE7YAAACkJmwBAABITdgCAACQmrAFAAAgNWELAABAasIWAACA1IQtAAAAqQlbAAAAUhO2AAAApCZsAQAASE3YAgAAkJqwBQAAIDVhCwAAQGrCFgAAgNSELQAAAKkJWwAAAFITtgAAAKQmbAEAAEhN2AIAAJCasAUAACA1YQsAAEBqwhYAAIDUhC0AAACpCVsAAABSE7YAAACkJmwBAABITdgCAACQmrAFAAAgNWELAABAasIWAACA1IRtiWzatCmmT58elZWVUVtbG7t27frY4/v6+mL9+vUxbdq0qKioiM9//vOxdevWEk0LAACQR/lID/Bp0NzcHKtWrYpNmzbFggUL4kc/+lEsXLgwDhw4ENdff/2Q5yxdujTeeeed2LJlS/zZn/1ZHDt2LE6dOlXiyQEAAEa/sqIoipEe4nI3b968mD17dmzevLl/bcaMGbFkyZJoamoadPzrr78e3/jGN+LQoUNx9dVXX9TP7O3tjerq6ujp6YmqqqqLnh0AAPjjvP8eWW5FHmYnT56Mtra2aGhoGLDe0NAQe/bsGfKc1157LebMmRPf//7347rrroubbropHnjggfj9739/zp/T19cXvb29Ax4AAACfBm5FHmbd3d1x+vTpqKmpGbBeU1MTXV1dQ55z6NCh2L17d1RWVsYrr7wS3d3d8a1vfSvefffdc37OtqmpKTZs2HDJ5wcAABjtXLEtkbKysgHPi6IYtHbWmTNnoqysLLZt2xZz586NRYsWxWOPPRbPPvvsOa/arlu3Lnp6evofR44cueSvAQAAYDRyxXaYTZw4McaOHTvo6uyxY8cGXcU9a9KkSXHddddFdXV1/9qMGTOiKIo4evRo3HjjjYPOqaioiIqKiks7PAAAQAKu2A6z8ePHR21tbbS2tg5Yb21tjbq6uiHPWbBgQfz2t7+N9957r3/tV7/6VYwZMyamTJkyrPMCAABkI2xLYM2aNfH000/H1q1b4+DBg7F69ero6OiIxsbGiPjoNuLly5f3H3/XXXfFhAkT4r777osDBw7Ezp0748EHH4y//du/jSuuuGKkXgYAAMCo5FbkEli2bFkcP348Nm7cGJ2dnTFz5sxoaWmJadOmRUREZ2dndHR09B//J3/yJ9Ha2hp/93d/F3PmzIkJEybE0qVL49FHHx2plwAAADBq+R7by5Tv0QIAgNLx/ntkuRUZAACA1IQtAAAAqQlbAAAAUhO2AAAApCZsAQAASE3YAgAAkJqwBQAAIDVhCwAAQGrCFgAAgNSELQAAAKkJWwAAAFITtgAAAKQmbAEAAEhN2AIAAJCasAUAACA1YQsAAEBqwhYAAIDUhC0AAACpCVsAAABSE7YAAACkJmwBAABITdgCAACQmrAFAAAgNWELAABAasIWAACA1IQtAAAAqQlbAAAAUhO2AAAApCZsAQAASE3YAgAAkJqwBQAAIDVhCwAAQGrCFgAAgNSELQAAAKkJWwAAAFITtgAAAKQmbAEAAEhN2AIAAJCasAUAACA1YQsAAEBqwhYAAIDUhC0AAACpCVsAAABSE7YAAACkJmwBAABITdgCAACQmrAFAAAgNWELAABAasIWAACA1IQtAAAAqQlbAAAAUhO2AAAApCZsAQAASE3YAgAAkJqwBQAAIDVhCwAAQGrCFgAAgNSELQAAAKkJWwAAAFITtiWyadOmmD59elRWVkZtbW3s2rXrvM574403ory8PL785S8P74AAAABJCdsSaG5ujlWrVsX69eujvb096uvrY+HChdHR0fGx5/X09MTy5cvja1/7WokmBQAAyKesKIpipIe43M2bNy9mz54dmzdv7l+bMWNGLFmyJJqams553je+8Y248cYbY+zYsfHqq6/G/v37z/tn9vb2RnV1dfT09ERVVdUnGR8AAPgjvP8eWa7YDrOTJ09GW1tbNDQ0DFhvaGiIPXv2nPO8Z555Jt5666145JFHzuvn9PX1RW9v74AHAADAp4GwHWbd3d1x+vTpqKmpGbBeU1MTXV1dQ57z61//OtauXRvbtm2L8vLy8/o5TU1NUV1d3f+YOnXqJ54dAAAgA2FbImVlZQOeF0UxaC0i4vTp03HXXXfFhg0b4qabbjrvP3/dunXR09PT/zhy5MgnnhkAACCD87scyEWbOHFijB07dtDV2WPHjg26ihsRceLEidi3b1+0t7fHd77znYiIOHPmTBRFEeXl5bF9+/a49dZbB51XUVERFRUVw/MiAAAARjFXbIfZ+PHjo7a2NlpbWwest7a2Rl1d3aDjq6qq4pe//GXs37+//9HY2Bhf+MIXYv/+/TFv3rxSjQ4AAJCCK7YlsGbNmrj77rtjzpw5MX/+/Pjxj38cHR0d0djYGBEf3Ub8m9/8Jn7yk5/EmDFjYubMmQPOv+aaa6KysnLQOgAAAMK2JJYtWxbHjx+PjRs3RmdnZ8ycOTNaWlpi2rRpERHR2dn5R7/TFgAAgKH5HtvLlO/RAgCA0vH+e2T5jC0AAACpCVsAAABSE7YAAACkJmwBAABITdgCAACQmrAFAAAgNWELAABAasIWAACA1IQtAAAAqQlbAAAAUhO2AAAApCZsAQAASE3YAgAAkJqwBQAAIDVhCwAAQGrCFgAAgNSELQAAAKkJWwAAAFITtgAAAKQmbAEAAEhN2AIAAJCasAUAACA1YQsAAEBqwhYAAIDUhC0AAACpCVsAAABSE7YAAACkJmwBAABITdgCAACQmrAFAAAgNWELAABAasIWAACA1IQtAAAAqQlbAAAAUhO2AAAApCZsAQAASE3YAgAAkJqwBQAAIDVhCwAAQGrCFgAAgNSELQAAAKkJWwAAAFITtgAAAKQmbAEAAEhN2AIAAJCasAUAACA1YQsAAEBqwhYAAIDUhC0AAACpCVsAAABSE7YAAACkJmwBAABITdgCAACQmrAFAAAgNWELAABAasIWAACA1IQtAAAAqQlbAAAAUhO2AAAApCZsS2TTpk0xffr0qKysjNra2ti1a9c5j3355Zfj9ttvj89+9rNRVVUV8+fPj5/97GclnBYAACAPYVsCzc3NsWrVqli/fn20t7dHfX19LFy4MDo6OoY8fufOnXH77bdHS0tLtLW1xS233BKLFy+O9vb2Ek8OAAAw+pUVRVGM9BCXu3nz5sXs2bNj8+bN/WszZsyIJUuWRFNT03n9GX/+538ey5Yti4cffvi8ju/t7Y3q6uro6emJqqqqi5obAAA4P95/jyxXbIfZyZMno62tLRoaGgasNzQ0xJ49e87rzzhz5kycOHEirr766nMe09fXF729vQMeAAAAnwbCdph1d3fH6dOno6amZsB6TU1NdHV1ndef8YMf/CDef//9WLp06TmPaWpqiurq6v7H1KlTP9HcAAAAWQjbEikrKxvwvCiKQWtDef755+N73/teNDc3xzXXXHPO49atWxc9PT39jyNHjnzimQEAADIoH+kBLncTJ06MsWPHDro6e+zYsUFXcf9Qc3NzrFixIl544YW47bbbPvbYioqKqKio+MTzAgAAZOOK7TAbP3581NbWRmtr64D11tbWqKurO+d5zz//fNx7773x3HPPxZ133jncYwIAAKTlim0JrFmzJu6+++6YM2dOzJ8/P3784x9HR0dHNDY2RsRHtxH/5je/iZ/85CcR8VHULl++PP71X/81vvKVr/Rf7b3iiiuiurp6xF4HAADAaCRsS2DZsmVx/Pjx2LhxY3R2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7WL20h86c+ZMnDhxIq6++urhGJEkLnYvPfPMM/HWW2/FI488MtwjksTF7KXXXnst5syZE9///vfjuuuui5tuuikeeOCB+P3vf1+KkRnFLmY/1dXVxdGjR6OlpSWKooh33nknXnzxxbjzzjtLMTKXEe+/S6t8pAfgwnV3d8fp06ejpqZmwHpNTU10dXUNeU5XV9eQx586dSq6u7tj0qRJwzYvo9fF7KU/9IMf/CDef//9WLp06XCMSBIXs5d+/etfx9q1a2PXrl1RXu7/jvjIxeylQ4cOxe7du6OysjJeeeWV6O7ujm9961vx7rvv+pztp9zF7Ke6urrYtm1bLFu2LP73f/83Tp06FX/1V38VP/zhD0sxMpcR779LyxXbxMrKygY8L4pi0NofO36odT59LnQvnfX888/H9773vWhubo5rrrlmuMYjkfPdS6dPn4677rorNmzYEDfddFOpxiORC/m9dObMmSgrK4tt27bF3LlzY9GiRfHYY4/Fs88+66otEXFh++nAgQOxcuXKePjhh6OtrS1ef/31OHz4cDQ2NpZiVC4z3n+Xjr8iT2jixIkxduzYQX/TeOzYsUF/K3TWtddeO+Tx5eXlMWHChGGbldHtYvbSWc3NzbFixYp44YUX4rbbbhvOMUngQvfSiRMnYt++fdHe3h7f+c53IuKjOCmKIsrLy2P79u1x6623lmR2RpeL+b00adKkuO6666K6urp/bcaMGVEURRw9ejRuvPHGYZ2Z0eti9lNTU1MsWLAgHnzwwYiI+NKXvhRXXXVV1NfXx6OPPuoqG+fN++/ScsU2ofHjx0dtbW20trYOWG9tbY26urohz5k/f/6g47dv3x5z5syJcePGDdusjG4Xs5ciPrpSe++998Zzzz3nM0dExIXvpaqqqvjlL38Z+/fv7380NjbGF77whdi/f3/MmzevVKMzylzM76UFCxbEb3/723jvvff61371q1/FmDFjYsqUKcM6L6PbxeynDz74IMaMGfgWeezYsRHx/6+2wfnw/rvERuhfWsUn9NOf/rQYN25csWXLluLAgQPFqlWriquuuqr4n//5n6IoimLt2rXF3Xff3X/8oUOHiiuvvLJYvXp1ceDAgWLLli3FuHHjihdffHGkXgKjxIXupeeee64oLy8vnnzyyaKzs7P/8bvf/W6kXgKjxIXupT/k34rMWRe6l06cOFFMmTKl+Ou//uvizTffLHbs2FHceOONxf333z9SL4FR5EL30zPPPFOUl5cXmzZtKt56661i9+7dxZw5c4q5c+eO1EtglDhx4kTR3t5etLe3FxFRPPbYY0V7e3vx9ttvF0Xh/fdIE7aJPfnkk8W0adOK8ePHF7Nnzy527NjR/5/dc889xVe/+tUBx//nf/5n8Rd/8RfF+PHji8997nPF5s2bSzwxo9WF7KWvfvWrRUQMetxzzz2lH5xR50J/L/1fwpb/60L30sGDB4vbbrutuOKKK4opU6YUa9asKT744IMST81odaH76fHHHy+++MUvFldccUUxadKk4m/+5m+Ko0ePlnhqRpv/+I//+Nj3QN5/j6yyonBPBQAAAHn5jC0AAACpCVsAAABSE7YAAACkJmwBAABITdgCAACQmrAFAAAgNWELAABAasIWAACA1IQtAAAAqQlbAAAAUhO2AAAApCZsAQAASE3YAgAAkJqwBQAAIDVhCwAAQGrCFgAAgNSELQAAAKkJWwAAAFITtgAAAKQmbAEAAEhN2AIAAJCasAUAACA1YQsAAEBqwhYAAIDUhC0AAACpCVsAAABSE7YAAACkJmwBAABITdgCAACQmrAFAAAgNWELAABAasIWAACA1IQtAAAAqQlbAAAAUhO2AAAApCZsAQAASE3YAgAAkJqwBQAAIDVhCwAAQGrCFgAAgNSELQAAAKkJWwAAAFITtgAAAKQmbAEAAEhN2AIAAJCasAUAACA1YQsAAEBqwhYAAIDUhC0AAACpCVsAAABSE7YAAACkJmwBAABITdgCAACQmrAFAAAgNWELAABAasIWAACA1IQtAAAAqQlbAAAAUhO2AAAApCZsAQAASE3YAgAAkJqwBQAAIDVhCwAAQGrCFgAAgNSELQAAAKkJWwAAAFITtgAAAKQmbAEAAEhN2AIAAJCasAUAACA1YQsAAEBqwhYAAIDUhC0AAACpCVsAAABSE7YAAACkJmwBAABITdgCAACQmrAFAAAgNWELAABAasIWAACA1IQtAAAAqQlbAAAAUhO2AAAApCZsAQAASE3YAgAAkJqwBQAAIDVhCwAAQGrCFgAAgNT+H/olNl1oxjotAAAAAElFTkSuQmCC", + "text/html": [ + "\n", + "
\n", + "
\n", + " Figure\n", + "
\n", + " \n", + "
\n", + " " + ], + "text/plain": [ + "Canvas(toolbar=Toolbar(toolitems=[('Home', 'Reset original view', 'home', 'home'), ('Back', 'Back to previous …" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'pressure'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/BBHRP/1bbhrpripbe1mcfarlane.c1.ipynb b/VAPs/quicklook/BBHRP/1bbhrpripbe1mcfarlane.c1.ipynb new file mode 100644 index 00000000..250c9efc --- /dev/null +++ b/VAPs/quicklook/BBHRP/1bbhrpripbe1mcfarlane.c1.ipynb @@ -0,0 +1,339 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# 1BBHRPRIPBE1MCFARLANE.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/bbhrp) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = '1bbhrpripbe1mcfarlane'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2011-06-05', 'facility': 'C1', 'site': 'sgp', 'start_date': '2002-03-01'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2011-06-03'\n", + "date_end = '2011-06-05'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['cloud_flag', 'trop_level', 'long_heating_rate']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'down_long_surf_flux'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'cloud_flag'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/BBHRP/30bbhrpripbe1mcfarlane.c1.ipynb b/VAPs/quicklook/BBHRP/30bbhrpripbe1mcfarlane.c1.ipynb new file mode 100644 index 00000000..e1d1d235 --- /dev/null +++ b/VAPs/quicklook/BBHRP/30bbhrpripbe1mcfarlane.c1.ipynb @@ -0,0 +1,339 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# 30BBHRPRIPBE1MCFARLANE.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/bbhrp) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = '30bbhrpripbe1mcfarlane'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2010-12-30', 'facility': 'C1', 'site': 'sgp', 'start_date': '2002-03-01'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2010-12-28'\n", + "date_end = '2010-12-30'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['long_heating_rate', 'long_heating_rate_std', 'long_heating_rate_frac']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'long_heating_rate'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'long_heating_rate'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/BBHRP/BBHRP_tutorial.ipynb b/VAPs/quicklook/BBHRP/BBHRP_tutorial.ipynb new file mode 100644 index 00000000..17e8097d --- /dev/null +++ b/VAPs/quicklook/BBHRP/BBHRP_tutorial.ipynb @@ -0,0 +1,838 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# 1BBHRPRIPBE1MCFARLANE.C1 Notebook\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/bbhrp) for more information about this vap." + ] + }, + { + "cell_type": "markdown", + "id": "97097763", + "metadata": {}, + "source": [ + "In this notebook, we demonstrate the workflow to explore ARM vap data (using 1bbhrpripbe1mcfarlane as an example.) Value-added products (VAPs) are higher-order data products that have been analyzed and processed to ease scientists’ use of ARM data in atmospheric research and global climate models. \n", + "Here is the main content we will cover." + ] + }, + { + "cell_type": "markdown", + "id": "eddec40f", + "metadata": {}, + "source": [ + "# Table of Content\n", + "## Access the data\n", + "* How to retrieve the data\n", + "* Data path and file name conventions\n", + "* Load data\n", + "## Explore the data\n", + "* NetCDF Data structure\n", + "* Xarray essentials\n", + "* Xarray Variable\n", + "## Plot the data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "import random\n", + "\n", + "import glob\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "2372d5be", + "metadata": {}, + "source": [ + "## Access the data" + ] + }, + { + "cell_type": "markdown", + "id": "5b0d9684", + "metadata": {}, + "source": [ + "### How to retrieve the data\n", + "We assume the path \"/data/archive\" is available where you are running this notebook. You can use `os.path.exists(\"/data/archive\")` to verify if the path exists at your machine.\n", + "\n", + "\n", + "### Data path and file name conventions\n", + "There several common terminologies regarding ARM data, for example, data-stream-name, data-level, site, facility, instrument, etc. (For more details, please see [ARM Data File Standards Version 1.2](https://www.google.com/search?q=arm+datastream+facility+cite+definition&rlz=1C1GCEJ_enUS1029US1029&ei=hb41ZICrBPukqtsPvMODIA&ved=0ahUKEwjAgeiS0aL-AhV7kmoFHbzhAAQQ4dUDCBA&uact=5&oq=arm+datastream+facility+cite+definition&gs_lcp=Cgxnd3Mtd2l6LXNlcnAQAzIFCAAQogQyBQgAEKIEMgUIABCiBDIFCAAQogQyBQgAEKIEOgoIABBHENYEELADOgoIIRCgARDDBBAKSgQIQRgAUJ4DWIoOYO0PaAFwAXgAgAGMAYgBuQmSAQMyLjmYAQCgAQHIAQjAAQE&sclient=gws-wiz-serp).) \n", + "\n", + "For example, this notebook is called `1bbhrpripbe1mcfarlane.c1`, where `1bbhrpripbe1mcfarlane` is the \"datastream name\", and the `{process.ds_class_level}}` is the \"data level\".\n", + "\n", + "This datastream also contains site `sgp` and facility `C1`. (Note: individual datastream might have multiple site-facility pairs.)\n", + "In such a case, the data of this data-stream is stored at `/data/archive/sgp/sgp1bbhrpripbe1mcfarlaneC1.c1`, which is in the format of `//.`. We can use the following method to assign the data-stream directory `datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )`\n", + "\n", + "The data files under datastream_dir also follows naming conventions. But once reach the datastream_dir level, the most import file naming convention to differentiate the files is \"yyyyMMdd.hhmmss\", which comes handy to filter out files based on datetime. For example, we can use `glob.glob(f'{datastream_dir}/*.200709*.*')` to filter files in 2007 September.\n", + "\n", + "(Note: refer to the https://adc.arm.gov/discovery/#/ and https://adc.arm.gov/solr8/metadata/select API to explore ARM datastream and assoicated available sites and facilities\n", + "\n", + "Please see the following examples in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d7e9eb85", + "metadata": {}, + "outputs": [], + "source": [ + "# Verify if DATA_DIR path exists\n", + "DATA_DIR = \"/data/archive\"\n", + "os.path.exists(DATA_DIR)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "586993fd", + "metadata": {}, + "outputs": [], + "source": [ + "# Speicify datastream_dir following the path conventions and check its existence\n", + "DATASTREAM_NAME = \"1bbhrpripbe1mcfarlane\"\n", + "DATA_LEVEL = \"c1\"\n", + "site = \"sgp\"\n", + "facility = \"C1\"\n", + "datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )\n", + "print(datastream_dir)\n", + "print(os.path.exists(datastream_dir))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0742f7c1", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: list 5 (random) files under datastream_dir\n", + "files = os.listdir(datastream_dir)\n", + "files[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39b98a36", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: get most recent file\n", + "list_of_files = glob.glob(f\"{datastream_dir}/*\") # * means all if need specific format then *.csv\n", + "latest_file = max(list_of_files, key=os.path.getctime)\n", + "latest_file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "902d514e", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus sort datastream files based on datetime\n", + "files = os.listdir(datastream_dir)\n", + "file_sorted = files.copy()\n", + "file_sorted.sort() \n", + "print(file_sorted[:5])\n", + "\n", + "# to reverse\n", + "file_sorted_reverse = files.copy() \n", + "file_sorted_reverse.sort(reverse=True)\n", + "print(file_sorted_reverse[:5])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec5923b2", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: pattern matching\n", + "# filter the 200709** files under datastream_dir\n", + "files_filter = glob.glob(f'{datastream_dir}/*.200709*.*')\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c4a4aa18", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: use armnotebook_utils.file_filter (TODO) to filter files based on datastream info \n" + ] + }, + { + "cell_type": "markdown", + "id": "cfeb9efc", + "metadata": {}, + "source": [ + "### Load data\n", + "The arm data is stored in [NetCDF](https://en.wikipedia.org/wiki/NetCDF#:~:text=%22NetCDF%20(network%20Common%20Data%20Form,format%20for%20representing%20scientific%20data.) format. We can use xarray's `open_dataset` method to load single file, or `open_mfdataset` to open multiple files. (Note: the latter will still return a single xarray dataset object by combining multiple files.)\n", + "\n", + "Note: open_dataset keeps the file handle open and lazy loads its contents. All parameters are passed directly to open_dataset. It is a preferable method over load_dataset for memory-effiency. (more details, please see [xarray.open_dataset](https://docs.xarray.dev/en/stable/generated/xarray.open_dataset.html))\n", + "\n", + "See the following example in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a440a329", + "metadata": {}, + "outputs": [], + "source": [ + "# open single file\n", + "full_path = latest_file\n", + "print(full_path)\n", + "ds_single = xr.open_dataset(full_path)\n", + "ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0143a3d", + "metadata": {}, + "outputs": [], + "source": [ + "print(ds_single)\n", + "print(type(ds_single))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c0f8939", + "metadata": {}, + "outputs": [], + "source": [ + "# open multiple files\n", + "n_files = 3\n", + "full_paths = [os.path.join(datastream_dir, f_path) for f_path in file_sorted_reverse[:n_files]]\n", + "print(full_paths)\n", + "try: # Note: sometimes multiple files cannot be merged, so we used try except here.\n", + " ds_mutiple = xr.open_mfdataset(full_paths)\n", + " # ds_mutiple\n", + " print(type(ds_mutiple))\n", + " print(ds_mutiple)\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "3fa59943", + "metadata": {}, + "source": [ + "## Explore the data" + ] + }, + { + "cell_type": "markdown", + "id": "571f69b9", + "metadata": {}, + "source": [ + "### NetCDF Data structure \n", + "\n", + "\n", + "(If you are confident with NetCDF basics and xarray essentials, feel free to skip this session.)\n", + "Before we dive into data exploration, there are some eseential concpets we should be familiar with (shown below): \n", + "* Dataset\n", + "* Data array\n", + "* Variable\n", + "* Dimenssion\n", + "* Coordinate\n", + "* Data Type\n", + "* Meta Data (Attributes)\n", + "\n", + "We will not go into details about NetCDF basics and here are some references you might find helpful\n", + "* [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#)\n", + "* [Network Common Data Form (NetCDF)](https://www.unidata.ucar.edu/software/netcdf/) and [A Brief History of (netCDF) Time](https://www.unidata.ucar.edu/software/netcdf/time/recs.html)\n", + "\n", + "\n", + "\n", + "### Xarray essentials\n", + "Earlier we introduced the xarray pacakge and used `open_dataset` and `open_mfdataset` to retrieve NetCDF data file as an xarray Dataset object. Recall that you can review the data in a notebook by using `print(ds)` or `ds`. \n", + "\n", + "Using xarray to retrieve the aforementioned NetCDF basics is straightforwared. In general,\n", + "* Dataset: `ds`\n", + "* Data array: `ds.variables`\n", + "* Variable: `ds.ds.variables`\n", + "* Dimenssion: `ds.dims`\n", + "* Coordinate: `ds.coords`\n", + "* Data Type: `type`\n", + "* Meta Data (Attributes)\n", + "\n", + "Also, here are some references if you are new to xarray\n", + "* [Xarray in 45 minutes](https://tutorial.xarray.dev/overview/xarray-in-45-min.html)\n", + "* [Handling NetCDF Files using XArray for Absolute Beginners](https://towardsdatascience.com/handling-netcdf-files-using-xarray-for-absolute-beginners-111a8ab4463f)\n", + "\n", + "Try the following commands in action\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b3eac323", + "metadata": {}, + "outputs": [], + "source": [ + "ds = ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77ecf85d", + "metadata": {}, + "outputs": [], + "source": [ + "# Dataset \n", + "ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25e7de09", + "metadata": {}, + "outputs": [], + "source": [ + "# Data array, Variable\n", + "# Note: the info can be overwhelming. Do not attempt to grasp everything at the first glance.\n", + "ds.variables" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c41b67e", + "metadata": {}, + "outputs": [], + "source": [ + "# dimenssions\n", + "ds.dims" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "156f1dfc", + "metadata": {}, + "outputs": [], + "source": [ + "# coordinates\n", + "ds.coords" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "277d6064", + "metadata": {}, + "outputs": [], + "source": [ + "# Meta Data (Attributes)\n", + "ds.attrs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d334681f", + "metadata": {}, + "outputs": [], + "source": [ + "# type\n", + "print(type(ds))\n", + "print(type(ds.variables))\n", + "print(type(ds.dims))\n", + "print(type(ds.coords))\n", + "print(type(ds.attrs))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "643399d6", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: retrieve variable names only\n", + "list(ds.variables)" + ] + }, + { + "cell_type": "markdown", + "id": "1cf36878", + "metadata": {}, + "source": [ + "#### Discussion: variable vs. coordinates vs. dimenssions. \n", + "From [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#):\n", + "\n", + "* Dimession: A dimension may be used to represent a real physical dimension, for example, time, latitude, longitude, or height. A dimension might also be used to index other quantities, for example station or model-run-number.\n", + "A netCDF dimension has both a name and a length. A dimension length is an arbitrary positive integer, except that one dimension in a netCDF dataset can have the length UNLIMITED.\n", + "\n", + "* Variables: Variables are used to store the bulk of the data in a netCDF dataset. A variable represents an array of values of the same type. A scalar value is treated as a 0-dimensional array. A variable has a name, a data type, and a shape described by its list of dimensions specified when the variable is created. A variable may also have associated attributes, which may be added, deleted or changed after the variable is created.\n", + "\n", + "* Coordinate (Variables): It is legal for a variable to have the same name as a dimension. Such variables have no special meaning to the netCDF library. However there is a convention that such variables should be treated in a special way by software using this library.\n", + "A variable with the same name as a dimension is called a coordinate variable. It typically defines a physical coordinate corresponding to that dimension.\n", + "\n", + "\n", + "Tips: By definition, coordinates and dimenssions are also variable. For example ds.time is a coordinate but it is also a special variable. But in practice, when we talk about variable, it implies regular/non-coordinate variable. We will use this convention for the remaining notebook. " + ] + }, + { + "cell_type": "markdown", + "id": "d47120f9", + "metadata": {}, + "source": [ + "### Xarray Variable\n", + "\n", + "\n", + "We can use `ds.variables` to access varialbes of a dataset. But many times viewing print-out the whole datasets can be overwhemling. Instead, we would work on the individual variable (also called Xarray data array.) using the `ds[var_name]` syntax, where `var_name` is the name of the variable. See the following examples in action.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5a57e136", + "metadata": {}, + "outputs": [], + "source": [ + "print(type(ds[\"time\"]))\n", + "ds[\"time\"]" + ] + }, + { + "cell_type": "markdown", + "id": "51ec643e", + "metadata": {}, + "source": [ + "#### Variable properties\n", + "For individual varible, the following properties are mostly used with the assocaited xarray method to retrieve them. (assuming var = ds[var_name])\n", + "* name: `var.name`\n", + "* data content: `var.data`\n", + "* attributes: `var.attrs`\n", + "* dimenstions: `var.dims`\n", + "* data type: `var.data.dtype`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a632a525", + "metadata": {}, + "outputs": [], + "source": [ + "var_name = \"time\"\n", + "var = ds[var_name]\n", + "\n", + "print(\"var.name: \\n\", var.name, \"\\n\")\n", + "print(\"var.data: \\n\", var.data, \"\\n\")\n", + "print(\"var.attrs: \\n\", var.attrs, \"\\n\")\n", + "print(\"var.dims: \\n\", var.dims, \"\\n\")\n", + "print(\"var.data.dtype: \\n\", var.data.dtype, \"\\n\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b256b07f", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: indivdual variable info table (collect and display variable info as a tabular format)\n", + "df_info = pd.DataFrame()\n", + "df_info[\"var_name\"] = list(ds.variables)\n", + "df_info[\"dims\"] = df_info.var_name.apply(lambda x: ds[x].dims)\n", + "df_info[\"is_dim\"] = df_info.var_name.apply(lambda x: x in ds.dims)\n", + "df_info[\"n_dim\"] = df_info.var_name.apply(lambda x: len(ds[x].dims))\n", + "df_info[\"attrs\"] = df_info.var_name.apply(lambda x: ds[x].attrs)\n", + "df_info[\"dtype\"] = df_info.var_name.apply(lambda x: ds[x].data.dtype)\n", + "df_info" + ] + }, + { + "cell_type": "markdown", + "id": "e6a36b96", + "metadata": {}, + "source": [ + "### Data cleaning/Preprocessing (skipped)\n", + "Data cleaning and preprocessing is an important stage in the the data analysis pipeline. However it will be out of the scope of this notebook. For more information about data cleaning and preprocessing basics, here are some references. \n", + "* [Xarray Fundamentals](https://earth-env-data-science.github.io/lectures/xarray/xarray.html)\n", + "* [Xarray Tutorial — Pangeo Gallery documentation](http://gallery.pangeo.io/repos/pangeo-data/pangeo-tutorial-gallery/xarray.html)\n", + "* [Pythonic Data Cleaning With pandas and NumPy](https://realpython.com/python-data-cleaning-numpy-pandas/)\n", + "* [Pandas - Cleaning Data](https://www.w3schools.com/python/pandas/pandas_cleaning.asp)" + ] + }, + { + "cell_type": "markdown", + "id": "ae6e1a69", + "metadata": {}, + "source": [ + "### Plotting\n", + "Data visualization (or plotting) is another important data analysis topic and deserves its own discussion. Here in this notebook we will only demonstrate basic tool for simple data visualization tasks.\n", + "\n", + "Here are some reference you might find useful:\n", + "* xarray plotting: https://docs.xarray.dev/en/stable/user-guide/plotting.html\n", + "* Atmospheric Community Toolkit (ACT): https://arm-doe.github.io/ACT/index.html\n", + "\n", + "Note: this notebook is auto-generated using a template. It uses a general idea to select variable(s) to plot and is not customized to each indivdual notebook. Feel free to change the variables in intrrests, especially certain figure is failed to plot." + ] + }, + { + "cell_type": "markdown", + "id": "7e417fb2", + "metadata": {}, + "source": [ + "#### 1-dimenssional basic time series plot\n", + "\n", + "For the following plot we would like to find variables such that\n", + "* it has one and only one dimession\n", + "* \"time\" is its coordinate variable\n", + "* it is not a dimenssion itself,\n", + "* it is not a special variable with substrings within [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "\n", + "Please see the following example in action. (For more details about pandas filtering, please see the following references.)\n", + "* [pandas: multiple conditions while indexing data frame](https://stackoverflow.com/questions/22591174/pandas-multiple-conditions-while-indexing-data-frame-unexpected-behavior)\n", + "* [pandas.DataFrame.apply](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.apply.html)\n", + "* [How to test if a string contains one of the substrings in a list...](https://stackoverflow.com/questions/26577516/how-to-test-if-a-string-contains-one-of-the-substrings-in-a-list-in-pandas" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30edac2d", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==1, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter = df_info[(df_info.n_dim == 1) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4777a659", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_1d = df_filter.var_name.values[0]\n", + " var_1d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb5ae985", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " ds[var_1d].plot()\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "9e41066e", + "metadata": {}, + "source": [ + "#### 2-dimenssional basic plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09bd4adc", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==2, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_2 = df_info[(df_info.n_dim == 2) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2cdea1a9", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_2d = df_filter_2.var_name.values[0]\n", + " var_2d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5f48879", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " print(ds[var_2d].dims)\n", + " # ds[var_2d].plot()\n", + "\n", + " # conventionally, use \"time\" as x-axis\n", + " ds[var_2d].plot(x=\"time\")\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "7554b2a6", + "metadata": {}, + "source": [ + "#### qc-plotting (optional)\n", + "\n", + "Note: act qc plotting has more strict requirement, one of them is the associated qc variable need to have \"flag_masks\" attributes. Which is added by using ds = act.io.armfiles.read_netcdf(files_list), then ds.clean.cleanup()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71c2096f", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " ds_act = act.io.armfiles.read_netcdf(full_path)\n", + " print(type(ds_act))\n", + " ds_act.clean.cleanup()\n", + "\n", + " # or \n", + " # ds.clean.cleanup()\n", + "except Exception as e:\n", + " print(\"ERROR\", e)\n", + " ds_act = ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "844f8505", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter valid variables for ACT qc plotting\n", + "condition = (\"qc_\" + df_info.var_name).apply(lambda x: ds_act[x].attrs.get(\"flag_masks\") is not None \n", + " if x in list(ds.variables) else False)\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_3 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings))) &\n", + " condition\n", + " ]\n", + "df_filter_3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd38cc6", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "try:\n", + " qc_variable = df_filter_3.var_name.values[0]\n", + " print(qc_variable)\n", + "except Exception as e:\n", + " print(e)\n", + " \n", + "try:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + " qc_ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "d76e4d27", + "metadata": {}, + "source": [ + "#### bonus: choose variables to plot from a dropdown menu " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb733804", + "metadata": {}, + "outputs": [], + "source": [ + "# Valid variables filtering\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_4 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82d5c20d", + "metadata": {}, + "outputs": [], + "source": [ + "# example 1: using xarray plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2fc9aaa4", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# fig, ax = plt.subplots(figsize=(10, 4))\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "# fig.clear() # Remove old lines from plot and plot new one\n", + "# if len(ds[var].dims)==2:\n", + "# ds[var].plot(x=\"time\", add_colorbar=False)\n", + "# else:\n", + "# ds[var].plot()\n", + "# plt.grid()\n", + "# plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fabdd802", + "metadata": {}, + "outputs": [], + "source": [ + "# example 2: using act plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "962b4186", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "\n", + "\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "\n", + "# i_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + "# i_display.add_subplots((1,), figsize=(10, 4))\n", + "# ax = i_display.plot(var, subplot_index=(0,), set_title=f\"{var} ({ds_act[var].attrs['long_name']})\",)\n", + "\n", + "# ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "# ax.grid()\n", + "# plt.show()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/BBHRP/bbhrpavg1mlawer.c1.ipynb b/VAPs/quicklook/BBHRP/bbhrpavg1mlawer.c1.ipynb new file mode 100644 index 00000000..7c0b6d4d --- /dev/null +++ b/VAPs/quicklook/BBHRP/bbhrpavg1mlawer.c1.ipynb @@ -0,0 +1,339 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# BBHRPAVG1MLAWER.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/bbhrp) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'bbhrpavg1mlawer'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2006-02-27', 'facility': 'C1', 'site': 'sgp', 'start_date': '2000-03-01'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2006-02-25'\n", + "date_end = '2006-02-27'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['pressure', 'temperature', 'column_ozone']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'flux'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'pressure'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/BEFLUX/BEFLUX_tutorial.ipynb b/VAPs/quicklook/BEFLUX/BEFLUX_tutorial.ipynb new file mode 100644 index 00000000..83809f02 --- /dev/null +++ b/VAPs/quicklook/BEFLUX/BEFLUX_tutorial.ipynb @@ -0,0 +1,838 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# BEFLUX1LONG.C1 Notebook\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/beflux) for more information about this vap." + ] + }, + { + "cell_type": "markdown", + "id": "97097763", + "metadata": {}, + "source": [ + "In this notebook, we demonstrate the workflow to explore ARM vap data (using beflux1long as an example.) Value-added products (VAPs) are higher-order data products that have been analyzed and processed to ease scientists’ use of ARM data in atmospheric research and global climate models. \n", + "Here is the main content we will cover." + ] + }, + { + "cell_type": "markdown", + "id": "eddec40f", + "metadata": {}, + "source": [ + "# Table of Content\n", + "## Access the data\n", + "* How to retrieve the data\n", + "* Data path and file name conventions\n", + "* Load data\n", + "## Explore the data\n", + "* NetCDF Data structure\n", + "* Xarray essentials\n", + "* Xarray Variable\n", + "## Plot the data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "import random\n", + "\n", + "import glob\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "2372d5be", + "metadata": {}, + "source": [ + "## Access the data" + ] + }, + { + "cell_type": "markdown", + "id": "5b0d9684", + "metadata": {}, + "source": [ + "### How to retrieve the data\n", + "We assume the path \"/data/archive\" is available where you are running this notebook. You can use `os.path.exists(\"/data/archive\")` to verify if the path exists at your machine.\n", + "\n", + "\n", + "### Data path and file name conventions\n", + "There several common terminologies regarding ARM data, for example, data-stream-name, data-level, site, facility, instrument, etc. (For more details, please see [ARM Data File Standards Version 1.2](https://www.google.com/search?q=arm+datastream+facility+cite+definition&rlz=1C1GCEJ_enUS1029US1029&ei=hb41ZICrBPukqtsPvMODIA&ved=0ahUKEwjAgeiS0aL-AhV7kmoFHbzhAAQQ4dUDCBA&uact=5&oq=arm+datastream+facility+cite+definition&gs_lcp=Cgxnd3Mtd2l6LXNlcnAQAzIFCAAQogQyBQgAEKIEMgUIABCiBDIFCAAQogQyBQgAEKIEOgoIABBHENYEELADOgoIIRCgARDDBBAKSgQIQRgAUJ4DWIoOYO0PaAFwAXgAgAGMAYgBuQmSAQMyLjmYAQCgAQHIAQjAAQE&sclient=gws-wiz-serp).) \n", + "\n", + "For example, this notebook is called `beflux1long.c1`, where `beflux1long` is the \"datastream name\", and the `{process.ds_class_level}}` is the \"data level\".\n", + "\n", + "This datastream also contains site `sgp` and facility `C1`. (Note: individual datastream might have multiple site-facility pairs.)\n", + "In such a case, the data of this data-stream is stored at `/data/archive/sgp/sgpbeflux1longC1.c1`, which is in the format of `//.`. We can use the following method to assign the data-stream directory `datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )`\n", + "\n", + "The data files under datastream_dir also follows naming conventions. But once reach the datastream_dir level, the most import file naming convention to differentiate the files is \"yyyyMMdd.hhmmss\", which comes handy to filter out files based on datetime. For example, we can use `glob.glob(f'{datastream_dir}/*.200709*.*')` to filter files in 2007 September.\n", + "\n", + "(Note: refer to the https://adc.arm.gov/discovery/#/ and https://adc.arm.gov/solr8/metadata/select API to explore ARM datastream and assoicated available sites and facilities\n", + "\n", + "Please see the following examples in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d7e9eb85", + "metadata": {}, + "outputs": [], + "source": [ + "# Verify if DATA_DIR path exists\n", + "DATA_DIR = \"/data/archive\"\n", + "os.path.exists(DATA_DIR)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "586993fd", + "metadata": {}, + "outputs": [], + "source": [ + "# Speicify datastream_dir following the path conventions and check its existence\n", + "DATASTREAM_NAME = \"beflux1long\"\n", + "DATA_LEVEL = \"c1\"\n", + "site = \"sgp\"\n", + "facility = \"C1\"\n", + "datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )\n", + "print(datastream_dir)\n", + "print(os.path.exists(datastream_dir))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0742f7c1", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: list 5 (random) files under datastream_dir\n", + "files = os.listdir(datastream_dir)\n", + "files[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39b98a36", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: get most recent file\n", + "list_of_files = glob.glob(f\"{datastream_dir}/*\") # * means all if need specific format then *.csv\n", + "latest_file = max(list_of_files, key=os.path.getctime)\n", + "latest_file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "902d514e", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus sort datastream files based on datetime\n", + "files = os.listdir(datastream_dir)\n", + "file_sorted = files.copy()\n", + "file_sorted.sort() \n", + "print(file_sorted[:5])\n", + "\n", + "# to reverse\n", + "file_sorted_reverse = files.copy() \n", + "file_sorted_reverse.sort(reverse=True)\n", + "print(file_sorted_reverse[:5])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec5923b2", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: pattern matching\n", + "# filter the 200709** files under datastream_dir\n", + "files_filter = glob.glob(f'{datastream_dir}/*.200709*.*')\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c4a4aa18", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: use armnotebook_utils.file_filter (TODO) to filter files based on datastream info \n" + ] + }, + { + "cell_type": "markdown", + "id": "cfeb9efc", + "metadata": {}, + "source": [ + "### Load data\n", + "The arm data is stored in [NetCDF](https://en.wikipedia.org/wiki/NetCDF#:~:text=%22NetCDF%20(network%20Common%20Data%20Form,format%20for%20representing%20scientific%20data.) format. We can use xarray's `open_dataset` method to load single file, or `open_mfdataset` to open multiple files. (Note: the latter will still return a single xarray dataset object by combining multiple files.)\n", + "\n", + "Note: open_dataset keeps the file handle open and lazy loads its contents. All parameters are passed directly to open_dataset. It is a preferable method over load_dataset for memory-effiency. (more details, please see [xarray.open_dataset](https://docs.xarray.dev/en/stable/generated/xarray.open_dataset.html))\n", + "\n", + "See the following example in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a440a329", + "metadata": {}, + "outputs": [], + "source": [ + "# open single file\n", + "full_path = latest_file\n", + "print(full_path)\n", + "ds_single = xr.open_dataset(full_path)\n", + "ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0143a3d", + "metadata": {}, + "outputs": [], + "source": [ + "print(ds_single)\n", + "print(type(ds_single))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c0f8939", + "metadata": {}, + "outputs": [], + "source": [ + "# open multiple files\n", + "n_files = 3\n", + "full_paths = [os.path.join(datastream_dir, f_path) for f_path in file_sorted_reverse[:n_files]]\n", + "print(full_paths)\n", + "try: # Note: sometimes multiple files cannot be merged, so we used try except here.\n", + " ds_mutiple = xr.open_mfdataset(full_paths)\n", + " # ds_mutiple\n", + " print(type(ds_mutiple))\n", + " print(ds_mutiple)\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "3fa59943", + "metadata": {}, + "source": [ + "## Explore the data" + ] + }, + { + "cell_type": "markdown", + "id": "571f69b9", + "metadata": {}, + "source": [ + "### NetCDF Data structure \n", + "\n", + "\n", + "(If you are confident with NetCDF basics and xarray essentials, feel free to skip this session.)\n", + "Before we dive into data exploration, there are some eseential concpets we should be familiar with (shown below): \n", + "* Dataset\n", + "* Data array\n", + "* Variable\n", + "* Dimenssion\n", + "* Coordinate\n", + "* Data Type\n", + "* Meta Data (Attributes)\n", + "\n", + "We will not go into details about NetCDF basics and here are some references you might find helpful\n", + "* [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#)\n", + "* [Network Common Data Form (NetCDF)](https://www.unidata.ucar.edu/software/netcdf/) and [A Brief History of (netCDF) Time](https://www.unidata.ucar.edu/software/netcdf/time/recs.html)\n", + "\n", + "\n", + "\n", + "### Xarray essentials\n", + "Earlier we introduced the xarray pacakge and used `open_dataset` and `open_mfdataset` to retrieve NetCDF data file as an xarray Dataset object. Recall that you can review the data in a notebook by using `print(ds)` or `ds`. \n", + "\n", + "Using xarray to retrieve the aforementioned NetCDF basics is straightforwared. In general,\n", + "* Dataset: `ds`\n", + "* Data array: `ds.variables`\n", + "* Variable: `ds.ds.variables`\n", + "* Dimenssion: `ds.dims`\n", + "* Coordinate: `ds.coords`\n", + "* Data Type: `type`\n", + "* Meta Data (Attributes)\n", + "\n", + "Also, here are some references if you are new to xarray\n", + "* [Xarray in 45 minutes](https://tutorial.xarray.dev/overview/xarray-in-45-min.html)\n", + "* [Handling NetCDF Files using XArray for Absolute Beginners](https://towardsdatascience.com/handling-netcdf-files-using-xarray-for-absolute-beginners-111a8ab4463f)\n", + "\n", + "Try the following commands in action\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b3eac323", + "metadata": {}, + "outputs": [], + "source": [ + "ds = ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77ecf85d", + "metadata": {}, + "outputs": [], + "source": [ + "# Dataset \n", + "ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25e7de09", + "metadata": {}, + "outputs": [], + "source": [ + "# Data array, Variable\n", + "# Note: the info can be overwhelming. Do not attempt to grasp everything at the first glance.\n", + "ds.variables" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c41b67e", + "metadata": {}, + "outputs": [], + "source": [ + "# dimenssions\n", + "ds.dims" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "156f1dfc", + "metadata": {}, + "outputs": [], + "source": [ + "# coordinates\n", + "ds.coords" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "277d6064", + "metadata": {}, + "outputs": [], + "source": [ + "# Meta Data (Attributes)\n", + "ds.attrs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d334681f", + "metadata": {}, + "outputs": [], + "source": [ + "# type\n", + "print(type(ds))\n", + "print(type(ds.variables))\n", + "print(type(ds.dims))\n", + "print(type(ds.coords))\n", + "print(type(ds.attrs))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "643399d6", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: retrieve variable names only\n", + "list(ds.variables)" + ] + }, + { + "cell_type": "markdown", + "id": "1cf36878", + "metadata": {}, + "source": [ + "#### Discussion: variable vs. coordinates vs. dimenssions. \n", + "From [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#):\n", + "\n", + "* Dimession: A dimension may be used to represent a real physical dimension, for example, time, latitude, longitude, or height. A dimension might also be used to index other quantities, for example station or model-run-number.\n", + "A netCDF dimension has both a name and a length. A dimension length is an arbitrary positive integer, except that one dimension in a netCDF dataset can have the length UNLIMITED.\n", + "\n", + "* Variables: Variables are used to store the bulk of the data in a netCDF dataset. A variable represents an array of values of the same type. A scalar value is treated as a 0-dimensional array. A variable has a name, a data type, and a shape described by its list of dimensions specified when the variable is created. A variable may also have associated attributes, which may be added, deleted or changed after the variable is created.\n", + "\n", + "* Coordinate (Variables): It is legal for a variable to have the same name as a dimension. Such variables have no special meaning to the netCDF library. However there is a convention that such variables should be treated in a special way by software using this library.\n", + "A variable with the same name as a dimension is called a coordinate variable. It typically defines a physical coordinate corresponding to that dimension.\n", + "\n", + "\n", + "Tips: By definition, coordinates and dimenssions are also variable. For example ds.time is a coordinate but it is also a special variable. But in practice, when we talk about variable, it implies regular/non-coordinate variable. We will use this convention for the remaining notebook. " + ] + }, + { + "cell_type": "markdown", + "id": "d47120f9", + "metadata": {}, + "source": [ + "### Xarray Variable\n", + "\n", + "\n", + "We can use `ds.variables` to access varialbes of a dataset. But many times viewing print-out the whole datasets can be overwhemling. Instead, we would work on the individual variable (also called Xarray data array.) using the `ds[var_name]` syntax, where `var_name` is the name of the variable. See the following examples in action.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5a57e136", + "metadata": {}, + "outputs": [], + "source": [ + "print(type(ds[\"time\"]))\n", + "ds[\"time\"]" + ] + }, + { + "cell_type": "markdown", + "id": "51ec643e", + "metadata": {}, + "source": [ + "#### Variable properties\n", + "For individual varible, the following properties are mostly used with the assocaited xarray method to retrieve them. (assuming var = ds[var_name])\n", + "* name: `var.name`\n", + "* data content: `var.data`\n", + "* attributes: `var.attrs`\n", + "* dimenstions: `var.dims`\n", + "* data type: `var.data.dtype`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a632a525", + "metadata": {}, + "outputs": [], + "source": [ + "var_name = \"time\"\n", + "var = ds[var_name]\n", + "\n", + "print(\"var.name: \\n\", var.name, \"\\n\")\n", + "print(\"var.data: \\n\", var.data, \"\\n\")\n", + "print(\"var.attrs: \\n\", var.attrs, \"\\n\")\n", + "print(\"var.dims: \\n\", var.dims, \"\\n\")\n", + "print(\"var.data.dtype: \\n\", var.data.dtype, \"\\n\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b256b07f", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: indivdual variable info table (collect and display variable info as a tabular format)\n", + "df_info = pd.DataFrame()\n", + "df_info[\"var_name\"] = list(ds.variables)\n", + "df_info[\"dims\"] = df_info.var_name.apply(lambda x: ds[x].dims)\n", + "df_info[\"is_dim\"] = df_info.var_name.apply(lambda x: x in ds.dims)\n", + "df_info[\"n_dim\"] = df_info.var_name.apply(lambda x: len(ds[x].dims))\n", + "df_info[\"attrs\"] = df_info.var_name.apply(lambda x: ds[x].attrs)\n", + "df_info[\"dtype\"] = df_info.var_name.apply(lambda x: ds[x].data.dtype)\n", + "df_info" + ] + }, + { + "cell_type": "markdown", + "id": "e6a36b96", + "metadata": {}, + "source": [ + "### Data cleaning/Preprocessing (skipped)\n", + "Data cleaning and preprocessing is an important stage in the the data analysis pipeline. However it will be out of the scope of this notebook. For more information about data cleaning and preprocessing basics, here are some references. \n", + "* [Xarray Fundamentals](https://earth-env-data-science.github.io/lectures/xarray/xarray.html)\n", + "* [Xarray Tutorial — Pangeo Gallery documentation](http://gallery.pangeo.io/repos/pangeo-data/pangeo-tutorial-gallery/xarray.html)\n", + "* [Pythonic Data Cleaning With pandas and NumPy](https://realpython.com/python-data-cleaning-numpy-pandas/)\n", + "* [Pandas - Cleaning Data](https://www.w3schools.com/python/pandas/pandas_cleaning.asp)" + ] + }, + { + "cell_type": "markdown", + "id": "ae6e1a69", + "metadata": {}, + "source": [ + "### Plotting\n", + "Data visualization (or plotting) is another important data analysis topic and deserves its own discussion. Here in this notebook we will only demonstrate basic tool for simple data visualization tasks.\n", + "\n", + "Here are some reference you might find useful:\n", + "* xarray plotting: https://docs.xarray.dev/en/stable/user-guide/plotting.html\n", + "* Atmospheric Community Toolkit (ACT): https://arm-doe.github.io/ACT/index.html\n", + "\n", + "Note: this notebook is auto-generated using a template. It uses a general idea to select variable(s) to plot and is not customized to each indivdual notebook. Feel free to change the variables in intrrests, especially certain figure is failed to plot." + ] + }, + { + "cell_type": "markdown", + "id": "7e417fb2", + "metadata": {}, + "source": [ + "#### 1-dimenssional basic time series plot\n", + "\n", + "For the following plot we would like to find variables such that\n", + "* it has one and only one dimession\n", + "* \"time\" is its coordinate variable\n", + "* it is not a dimenssion itself,\n", + "* it is not a special variable with substrings within [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "\n", + "Please see the following example in action. (For more details about pandas filtering, please see the following references.)\n", + "* [pandas: multiple conditions while indexing data frame](https://stackoverflow.com/questions/22591174/pandas-multiple-conditions-while-indexing-data-frame-unexpected-behavior)\n", + "* [pandas.DataFrame.apply](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.apply.html)\n", + "* [How to test if a string contains one of the substrings in a list...](https://stackoverflow.com/questions/26577516/how-to-test-if-a-string-contains-one-of-the-substrings-in-a-list-in-pandas" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30edac2d", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==1, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter = df_info[(df_info.n_dim == 1) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4777a659", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_1d = df_filter.var_name.values[0]\n", + " var_1d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb5ae985", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " ds[var_1d].plot()\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "9e41066e", + "metadata": {}, + "source": [ + "#### 2-dimenssional basic plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09bd4adc", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==2, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_2 = df_info[(df_info.n_dim == 2) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2cdea1a9", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_2d = df_filter_2.var_name.values[0]\n", + " var_2d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5f48879", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " print(ds[var_2d].dims)\n", + " # ds[var_2d].plot()\n", + "\n", + " # conventionally, use \"time\" as x-axis\n", + " ds[var_2d].plot(x=\"time\")\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "7554b2a6", + "metadata": {}, + "source": [ + "#### qc-plotting (optional)\n", + "\n", + "Note: act qc plotting has more strict requirement, one of them is the associated qc variable need to have \"flag_masks\" attributes. Which is added by using ds = act.io.armfiles.read_netcdf(files_list), then ds.clean.cleanup()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71c2096f", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " ds_act = act.io.armfiles.read_netcdf(full_path)\n", + " print(type(ds_act))\n", + " ds_act.clean.cleanup()\n", + "\n", + " # or \n", + " # ds.clean.cleanup()\n", + "except Exception as e:\n", + " print(\"ERROR\", e)\n", + " ds_act = ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "844f8505", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter valid variables for ACT qc plotting\n", + "condition = (\"qc_\" + df_info.var_name).apply(lambda x: ds_act[x].attrs.get(\"flag_masks\") is not None \n", + " if x in list(ds.variables) else False)\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_3 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings))) &\n", + " condition\n", + " ]\n", + "df_filter_3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd38cc6", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "try:\n", + " qc_variable = df_filter_3.var_name.values[0]\n", + " print(qc_variable)\n", + "except Exception as e:\n", + " print(e)\n", + " \n", + "try:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + " qc_ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "d76e4d27", + "metadata": {}, + "source": [ + "#### bonus: choose variables to plot from a dropdown menu " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb733804", + "metadata": {}, + "outputs": [], + "source": [ + "# Valid variables filtering\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_4 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82d5c20d", + "metadata": {}, + "outputs": [], + "source": [ + "# example 1: using xarray plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2fc9aaa4", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# fig, ax = plt.subplots(figsize=(10, 4))\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "# fig.clear() # Remove old lines from plot and plot new one\n", + "# if len(ds[var].dims)==2:\n", + "# ds[var].plot(x=\"time\", add_colorbar=False)\n", + "# else:\n", + "# ds[var].plot()\n", + "# plt.grid()\n", + "# plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fabdd802", + "metadata": {}, + "outputs": [], + "source": [ + "# example 2: using act plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "962b4186", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "\n", + "\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "\n", + "# i_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + "# i_display.add_subplots((1,), figsize=(10, 4))\n", + "# ax = i_display.plot(var, subplot_index=(0,), set_title=f\"{var} ({ds_act[var].attrs['long_name']})\",)\n", + "\n", + "# ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "# ax.grid()\n", + "# plt.show()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/BEFLUX/beflux1long.c1.ipynb b/VAPs/quicklook/BEFLUX/beflux1long.c1.ipynb new file mode 100644 index 00000000..02a9622a --- /dev/null +++ b/VAPs/quicklook/BEFLUX/beflux1long.c1.ipynb @@ -0,0 +1,297 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# BEFLUX1LONG.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/beflux) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'beflux1long'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2023-12-18', 'facility': 'C1', 'site': 'sgp', 'start_date': '1995-05-19'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2023-12-16'\n", + "date_end = '2023-12-18'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['down_short_hemisp', 'down_long_hemisp', 'short_direct_normal']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'down_short_hemisp'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/BEFLUX/qcflux1long.c1.ipynb b/VAPs/quicklook/BEFLUX/qcflux1long.c1.ipynb new file mode 100644 index 00000000..c8ae1ac1 --- /dev/null +++ b/VAPs/quicklook/BEFLUX/qcflux1long.c1.ipynb @@ -0,0 +1,297 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# QCFLUX1LONG.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/beflux) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'qcflux1long'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2023-12-18', 'facility': 'C1', 'site': 'sgp', 'start_date': '1997-03-21'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2023-12-16'\n", + "date_end = '2023-12-18'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['down_short_hemisp', 'down_long_hemisp', 'short_direct_normal']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'down_short_hemisp'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/CCNKAPPA/CCNKAPPA_tutorial.ipynb b/VAPs/quicklook/CCNKAPPA/CCNKAPPA_tutorial.ipynb new file mode 100644 index 00000000..9d9602fc --- /dev/null +++ b/VAPs/quicklook/CCNKAPPA/CCNKAPPA_tutorial.ipynb @@ -0,0 +1,838 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# AOSCCNSMPSKAPPA.C1 Notebook\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/ccnkappa) for more information about this vap." + ] + }, + { + "cell_type": "markdown", + "id": "97097763", + "metadata": {}, + "source": [ + "In this notebook, we demonstrate the workflow to explore ARM vap data (using aosccnsmpskappa as an example.) Value-added products (VAPs) are higher-order data products that have been analyzed and processed to ease scientists’ use of ARM data in atmospheric research and global climate models. \n", + "Here is the main content we will cover." + ] + }, + { + "cell_type": "markdown", + "id": "eddec40f", + "metadata": {}, + "source": [ + "# Table of Content\n", + "## Access the data\n", + "* How to retrieve the data\n", + "* Data path and file name conventions\n", + "* Load data\n", + "## Explore the data\n", + "* NetCDF Data structure\n", + "* Xarray essentials\n", + "* Xarray Variable\n", + "## Plot the data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "import random\n", + "\n", + "import glob\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "2372d5be", + "metadata": {}, + "source": [ + "## Access the data" + ] + }, + { + "cell_type": "markdown", + "id": "5b0d9684", + "metadata": {}, + "source": [ + "### How to retrieve the data\n", + "We assume the path \"/data/archive\" is available where you are running this notebook. You can use `os.path.exists(\"/data/archive\")` to verify if the path exists at your machine.\n", + "\n", + "\n", + "### Data path and file name conventions\n", + "There several common terminologies regarding ARM data, for example, data-stream-name, data-level, site, facility, instrument, etc. (For more details, please see [ARM Data File Standards Version 1.2](https://www.google.com/search?q=arm+datastream+facility+cite+definition&rlz=1C1GCEJ_enUS1029US1029&ei=hb41ZICrBPukqtsPvMODIA&ved=0ahUKEwjAgeiS0aL-AhV7kmoFHbzhAAQQ4dUDCBA&uact=5&oq=arm+datastream+facility+cite+definition&gs_lcp=Cgxnd3Mtd2l6LXNlcnAQAzIFCAAQogQyBQgAEKIEMgUIABCiBDIFCAAQogQyBQgAEKIEOgoIABBHENYEELADOgoIIRCgARDDBBAKSgQIQRgAUJ4DWIoOYO0PaAFwAXgAgAGMAYgBuQmSAQMyLjmYAQCgAQHIAQjAAQE&sclient=gws-wiz-serp).) \n", + "\n", + "For example, this notebook is called `aosccnsmpskappa.c1`, where `aosccnsmpskappa` is the \"datastream name\", and the `{process.ds_class_level}}` is the \"data level\".\n", + "\n", + "This datastream also contains site `cor` and facility `M1`. (Note: individual datastream might have multiple site-facility pairs.)\n", + "In such a case, the data of this data-stream is stored at `/data/archive/cor/coraosccnsmpskappaM1.c1`, which is in the format of `//.`. We can use the following method to assign the data-stream directory `datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )`\n", + "\n", + "The data files under datastream_dir also follows naming conventions. But once reach the datastream_dir level, the most import file naming convention to differentiate the files is \"yyyyMMdd.hhmmss\", which comes handy to filter out files based on datetime. For example, we can use `glob.glob(f'{datastream_dir}/*.200709*.*')` to filter files in 2007 September.\n", + "\n", + "(Note: refer to the https://adc.arm.gov/discovery/#/ and https://adc.arm.gov/solr8/metadata/select API to explore ARM datastream and assoicated available sites and facilities\n", + "\n", + "Please see the following examples in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d7e9eb85", + "metadata": {}, + "outputs": [], + "source": [ + "# Verify if DATA_DIR path exists\n", + "DATA_DIR = \"/data/archive\"\n", + "os.path.exists(DATA_DIR)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "586993fd", + "metadata": {}, + "outputs": [], + "source": [ + "# Speicify datastream_dir following the path conventions and check its existence\n", + "DATASTREAM_NAME = \"aosccnsmpskappa\"\n", + "DATA_LEVEL = \"c1\"\n", + "site = \"cor\"\n", + "facility = \"M1\"\n", + "datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )\n", + "print(datastream_dir)\n", + "print(os.path.exists(datastream_dir))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0742f7c1", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: list 5 (random) files under datastream_dir\n", + "files = os.listdir(datastream_dir)\n", + "files[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39b98a36", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: get most recent file\n", + "list_of_files = glob.glob(f\"{datastream_dir}/*\") # * means all if need specific format then *.csv\n", + "latest_file = max(list_of_files, key=os.path.getctime)\n", + "latest_file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "902d514e", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus sort datastream files based on datetime\n", + "files = os.listdir(datastream_dir)\n", + "file_sorted = files.copy()\n", + "file_sorted.sort() \n", + "print(file_sorted[:5])\n", + "\n", + "# to reverse\n", + "file_sorted_reverse = files.copy() \n", + "file_sorted_reverse.sort(reverse=True)\n", + "print(file_sorted_reverse[:5])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec5923b2", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: pattern matching\n", + "# filter the 200709** files under datastream_dir\n", + "files_filter = glob.glob(f'{datastream_dir}/*.200709*.*')\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c4a4aa18", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: use armnotebook_utils.file_filter (TODO) to filter files based on datastream info \n" + ] + }, + { + "cell_type": "markdown", + "id": "cfeb9efc", + "metadata": {}, + "source": [ + "### Load data\n", + "The arm data is stored in [NetCDF](https://en.wikipedia.org/wiki/NetCDF#:~:text=%22NetCDF%20(network%20Common%20Data%20Form,format%20for%20representing%20scientific%20data.) format. We can use xarray's `open_dataset` method to load single file, or `open_mfdataset` to open multiple files. (Note: the latter will still return a single xarray dataset object by combining multiple files.)\n", + "\n", + "Note: open_dataset keeps the file handle open and lazy loads its contents. All parameters are passed directly to open_dataset. It is a preferable method over load_dataset for memory-effiency. (more details, please see [xarray.open_dataset](https://docs.xarray.dev/en/stable/generated/xarray.open_dataset.html))\n", + "\n", + "See the following example in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a440a329", + "metadata": {}, + "outputs": [], + "source": [ + "# open single file\n", + "full_path = latest_file\n", + "print(full_path)\n", + "ds_single = xr.open_dataset(full_path)\n", + "ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0143a3d", + "metadata": {}, + "outputs": [], + "source": [ + "print(ds_single)\n", + "print(type(ds_single))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c0f8939", + "metadata": {}, + "outputs": [], + "source": [ + "# open multiple files\n", + "n_files = 3\n", + "full_paths = [os.path.join(datastream_dir, f_path) for f_path in file_sorted_reverse[:n_files]]\n", + "print(full_paths)\n", + "try: # Note: sometimes multiple files cannot be merged, so we used try except here.\n", + " ds_mutiple = xr.open_mfdataset(full_paths)\n", + " # ds_mutiple\n", + " print(type(ds_mutiple))\n", + " print(ds_mutiple)\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "3fa59943", + "metadata": {}, + "source": [ + "## Explore the data" + ] + }, + { + "cell_type": "markdown", + "id": "571f69b9", + "metadata": {}, + "source": [ + "### NetCDF Data structure \n", + "\n", + "\n", + "(If you are confident with NetCDF basics and xarray essentials, feel free to skip this session.)\n", + "Before we dive into data exploration, there are some eseential concpets we should be familiar with (shown below): \n", + "* Dataset\n", + "* Data array\n", + "* Variable\n", + "* Dimenssion\n", + "* Coordinate\n", + "* Data Type\n", + "* Meta Data (Attributes)\n", + "\n", + "We will not go into details about NetCDF basics and here are some references you might find helpful\n", + "* [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#)\n", + "* [Network Common Data Form (NetCDF)](https://www.unidata.ucar.edu/software/netcdf/) and [A Brief History of (netCDF) Time](https://www.unidata.ucar.edu/software/netcdf/time/recs.html)\n", + "\n", + "\n", + "\n", + "### Xarray essentials\n", + "Earlier we introduced the xarray pacakge and used `open_dataset` and `open_mfdataset` to retrieve NetCDF data file as an xarray Dataset object. Recall that you can review the data in a notebook by using `print(ds)` or `ds`. \n", + "\n", + "Using xarray to retrieve the aforementioned NetCDF basics is straightforwared. In general,\n", + "* Dataset: `ds`\n", + "* Data array: `ds.variables`\n", + "* Variable: `ds.ds.variables`\n", + "* Dimenssion: `ds.dims`\n", + "* Coordinate: `ds.coords`\n", + "* Data Type: `type`\n", + "* Meta Data (Attributes)\n", + "\n", + "Also, here are some references if you are new to xarray\n", + "* [Xarray in 45 minutes](https://tutorial.xarray.dev/overview/xarray-in-45-min.html)\n", + "* [Handling NetCDF Files using XArray for Absolute Beginners](https://towardsdatascience.com/handling-netcdf-files-using-xarray-for-absolute-beginners-111a8ab4463f)\n", + "\n", + "Try the following commands in action\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b3eac323", + "metadata": {}, + "outputs": [], + "source": [ + "ds = ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77ecf85d", + "metadata": {}, + "outputs": [], + "source": [ + "# Dataset \n", + "ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25e7de09", + "metadata": {}, + "outputs": [], + "source": [ + "# Data array, Variable\n", + "# Note: the info can be overwhelming. Do not attempt to grasp everything at the first glance.\n", + "ds.variables" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c41b67e", + "metadata": {}, + "outputs": [], + "source": [ + "# dimenssions\n", + "ds.dims" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "156f1dfc", + "metadata": {}, + "outputs": [], + "source": [ + "# coordinates\n", + "ds.coords" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "277d6064", + "metadata": {}, + "outputs": [], + "source": [ + "# Meta Data (Attributes)\n", + "ds.attrs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d334681f", + "metadata": {}, + "outputs": [], + "source": [ + "# type\n", + "print(type(ds))\n", + "print(type(ds.variables))\n", + "print(type(ds.dims))\n", + "print(type(ds.coords))\n", + "print(type(ds.attrs))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "643399d6", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: retrieve variable names only\n", + "list(ds.variables)" + ] + }, + { + "cell_type": "markdown", + "id": "1cf36878", + "metadata": {}, + "source": [ + "#### Discussion: variable vs. coordinates vs. dimenssions. \n", + "From [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#):\n", + "\n", + "* Dimession: A dimension may be used to represent a real physical dimension, for example, time, latitude, longitude, or height. A dimension might also be used to index other quantities, for example station or model-run-number.\n", + "A netCDF dimension has both a name and a length. A dimension length is an arbitrary positive integer, except that one dimension in a netCDF dataset can have the length UNLIMITED.\n", + "\n", + "* Variables: Variables are used to store the bulk of the data in a netCDF dataset. A variable represents an array of values of the same type. A scalar value is treated as a 0-dimensional array. A variable has a name, a data type, and a shape described by its list of dimensions specified when the variable is created. A variable may also have associated attributes, which may be added, deleted or changed after the variable is created.\n", + "\n", + "* Coordinate (Variables): It is legal for a variable to have the same name as a dimension. Such variables have no special meaning to the netCDF library. However there is a convention that such variables should be treated in a special way by software using this library.\n", + "A variable with the same name as a dimension is called a coordinate variable. It typically defines a physical coordinate corresponding to that dimension.\n", + "\n", + "\n", + "Tips: By definition, coordinates and dimenssions are also variable. For example ds.time is a coordinate but it is also a special variable. But in practice, when we talk about variable, it implies regular/non-coordinate variable. We will use this convention for the remaining notebook. " + ] + }, + { + "cell_type": "markdown", + "id": "d47120f9", + "metadata": {}, + "source": [ + "### Xarray Variable\n", + "\n", + "\n", + "We can use `ds.variables` to access varialbes of a dataset. But many times viewing print-out the whole datasets can be overwhemling. Instead, we would work on the individual variable (also called Xarray data array.) using the `ds[var_name]` syntax, where `var_name` is the name of the variable. See the following examples in action.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5a57e136", + "metadata": {}, + "outputs": [], + "source": [ + "print(type(ds[\"time\"]))\n", + "ds[\"time\"]" + ] + }, + { + "cell_type": "markdown", + "id": "51ec643e", + "metadata": {}, + "source": [ + "#### Variable properties\n", + "For individual varible, the following properties are mostly used with the assocaited xarray method to retrieve them. (assuming var = ds[var_name])\n", + "* name: `var.name`\n", + "* data content: `var.data`\n", + "* attributes: `var.attrs`\n", + "* dimenstions: `var.dims`\n", + "* data type: `var.data.dtype`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a632a525", + "metadata": {}, + "outputs": [], + "source": [ + "var_name = \"time\"\n", + "var = ds[var_name]\n", + "\n", + "print(\"var.name: \\n\", var.name, \"\\n\")\n", + "print(\"var.data: \\n\", var.data, \"\\n\")\n", + "print(\"var.attrs: \\n\", var.attrs, \"\\n\")\n", + "print(\"var.dims: \\n\", var.dims, \"\\n\")\n", + "print(\"var.data.dtype: \\n\", var.data.dtype, \"\\n\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b256b07f", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: indivdual variable info table (collect and display variable info as a tabular format)\n", + "df_info = pd.DataFrame()\n", + "df_info[\"var_name\"] = list(ds.variables)\n", + "df_info[\"dims\"] = df_info.var_name.apply(lambda x: ds[x].dims)\n", + "df_info[\"is_dim\"] = df_info.var_name.apply(lambda x: x in ds.dims)\n", + "df_info[\"n_dim\"] = df_info.var_name.apply(lambda x: len(ds[x].dims))\n", + "df_info[\"attrs\"] = df_info.var_name.apply(lambda x: ds[x].attrs)\n", + "df_info[\"dtype\"] = df_info.var_name.apply(lambda x: ds[x].data.dtype)\n", + "df_info" + ] + }, + { + "cell_type": "markdown", + "id": "e6a36b96", + "metadata": {}, + "source": [ + "### Data cleaning/Preprocessing (skipped)\n", + "Data cleaning and preprocessing is an important stage in the the data analysis pipeline. However it will be out of the scope of this notebook. For more information about data cleaning and preprocessing basics, here are some references. \n", + "* [Xarray Fundamentals](https://earth-env-data-science.github.io/lectures/xarray/xarray.html)\n", + "* [Xarray Tutorial — Pangeo Gallery documentation](http://gallery.pangeo.io/repos/pangeo-data/pangeo-tutorial-gallery/xarray.html)\n", + "* [Pythonic Data Cleaning With pandas and NumPy](https://realpython.com/python-data-cleaning-numpy-pandas/)\n", + "* [Pandas - Cleaning Data](https://www.w3schools.com/python/pandas/pandas_cleaning.asp)" + ] + }, + { + "cell_type": "markdown", + "id": "ae6e1a69", + "metadata": {}, + "source": [ + "### Plotting\n", + "Data visualization (or plotting) is another important data analysis topic and deserves its own discussion. Here in this notebook we will only demonstrate basic tool for simple data visualization tasks.\n", + "\n", + "Here are some reference you might find useful:\n", + "* xarray plotting: https://docs.xarray.dev/en/stable/user-guide/plotting.html\n", + "* Atmospheric Community Toolkit (ACT): https://arm-doe.github.io/ACT/index.html\n", + "\n", + "Note: this notebook is auto-generated using a template. It uses a general idea to select variable(s) to plot and is not customized to each indivdual notebook. Feel free to change the variables in intrrests, especially certain figure is failed to plot." + ] + }, + { + "cell_type": "markdown", + "id": "7e417fb2", + "metadata": {}, + "source": [ + "#### 1-dimenssional basic time series plot\n", + "\n", + "For the following plot we would like to find variables such that\n", + "* it has one and only one dimession\n", + "* \"time\" is its coordinate variable\n", + "* it is not a dimenssion itself,\n", + "* it is not a special variable with substrings within [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "\n", + "Please see the following example in action. (For more details about pandas filtering, please see the following references.)\n", + "* [pandas: multiple conditions while indexing data frame](https://stackoverflow.com/questions/22591174/pandas-multiple-conditions-while-indexing-data-frame-unexpected-behavior)\n", + "* [pandas.DataFrame.apply](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.apply.html)\n", + "* [How to test if a string contains one of the substrings in a list...](https://stackoverflow.com/questions/26577516/how-to-test-if-a-string-contains-one-of-the-substrings-in-a-list-in-pandas" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30edac2d", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==1, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter = df_info[(df_info.n_dim == 1) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4777a659", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_1d = df_filter.var_name.values[0]\n", + " var_1d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb5ae985", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " ds[var_1d].plot()\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "9e41066e", + "metadata": {}, + "source": [ + "#### 2-dimenssional basic plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09bd4adc", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==2, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_2 = df_info[(df_info.n_dim == 2) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2cdea1a9", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_2d = df_filter_2.var_name.values[0]\n", + " var_2d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5f48879", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " print(ds[var_2d].dims)\n", + " # ds[var_2d].plot()\n", + "\n", + " # conventionally, use \"time\" as x-axis\n", + " ds[var_2d].plot(x=\"time\")\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "7554b2a6", + "metadata": {}, + "source": [ + "#### qc-plotting (optional)\n", + "\n", + "Note: act qc plotting has more strict requirement, one of them is the associated qc variable need to have \"flag_masks\" attributes. Which is added by using ds = act.io.armfiles.read_netcdf(files_list), then ds.clean.cleanup()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71c2096f", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " ds_act = act.io.armfiles.read_netcdf(full_path)\n", + " print(type(ds_act))\n", + " ds_act.clean.cleanup()\n", + "\n", + " # or \n", + " # ds.clean.cleanup()\n", + "except Exception as e:\n", + " print(\"ERROR\", e)\n", + " ds_act = ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "844f8505", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter valid variables for ACT qc plotting\n", + "condition = (\"qc_\" + df_info.var_name).apply(lambda x: ds_act[x].attrs.get(\"flag_masks\") is not None \n", + " if x in list(ds.variables) else False)\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_3 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings))) &\n", + " condition\n", + " ]\n", + "df_filter_3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd38cc6", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "try:\n", + " qc_variable = df_filter_3.var_name.values[0]\n", + " print(qc_variable)\n", + "except Exception as e:\n", + " print(e)\n", + " \n", + "try:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + " qc_ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "d76e4d27", + "metadata": {}, + "source": [ + "#### bonus: choose variables to plot from a dropdown menu " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb733804", + "metadata": {}, + "outputs": [], + "source": [ + "# Valid variables filtering\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_4 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82d5c20d", + "metadata": {}, + "outputs": [], + "source": [ + "# example 1: using xarray plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2fc9aaa4", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# fig, ax = plt.subplots(figsize=(10, 4))\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "# fig.clear() # Remove old lines from plot and plot new one\n", + "# if len(ds[var].dims)==2:\n", + "# ds[var].plot(x=\"time\", add_colorbar=False)\n", + "# else:\n", + "# ds[var].plot()\n", + "# plt.grid()\n", + "# plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fabdd802", + "metadata": {}, + "outputs": [], + "source": [ + "# example 2: using act plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "962b4186", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "\n", + "\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "\n", + "# i_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + "# i_display.add_subplots((1,), figsize=(10, 4))\n", + "# ax = i_display.plot(var, subplot_index=(0,), set_title=f\"{var} ({ds_act[var].attrs['long_name']})\",)\n", + "\n", + "# ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "# ax.grid()\n", + "# plt.show()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/CCNKAPPA/aosccnsmpskappa.c1.ipynb b/VAPs/quicklook/CCNKAPPA/aosccnsmpskappa.c1.ipynb new file mode 100644 index 00000000..4a49117e --- /dev/null +++ b/VAPs/quicklook/CCNKAPPA/aosccnsmpskappa.c1.ipynb @@ -0,0 +1,339 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# AOSCCNSMPSKAPPA.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/ccnkappa) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'aosccnsmpskappa'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2019-04-30', 'facility': 'M1', 'site': 'cor', 'start_date': '2018-09-28'}, {'end_date': '2017-10-20', 'facility': 'M1', 'site': 'asi', 'start_date': '2016-05-20'}, {'end_date': '2020-04-23', 'facility': 'M1', 'site': 'anx', 'start_date': '2019-12-01'}, {'end_date': '2023-09-16', 'facility': 'M1', 'site': 'epc', 'start_date': '2023-05-23'}, {'end_date': '2022-09-02', 'facility': 'M1', 'site': 'hou', 'start_date': '2021-09-15'}, {'end_date': '2023-08-23', 'facility': 'C1', 'site': 'ena', 'start_date': '2022-11-01'}, {'end_date': '2021-10-15', 'facility': 'M1', 'site': 'guc', 'start_date': '2021-09-01'}, {'end_date': '2020-10-01', 'facility': 'M1', 'site': 'mos', 'start_date': '2019-10-09'}, {'end_date': '2023-08-18', 'facility': 'E13', 'site': 'sgp', 'start_date': '2017-04-12'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'E13' )\n", + "\n", + "date_start = '2023-08-16'\n", + "date_end = '2023-08-18'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['kappa', 'critical_diameter', 'aerosol_number_concentration']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'kappa'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'kappa'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/CCNPROF/.ipynb_checkpoints/rlccnprof1ghan.c1-checkpoint.ipynb b/VAPs/quicklook/CCNPROF/.ipynb_checkpoints/rlccnprof1ghan.c1-checkpoint.ipynb new file mode 100644 index 00000000..696f23b7 --- /dev/null +++ b/VAPs/quicklook/CCNPROF/.ipynb_checkpoints/rlccnprof1ghan.c1-checkpoint.ipynb @@ -0,0 +1,4109 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# RLCCNPROF1GHAN.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/ccnprof) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'rlccnprof1ghan'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2014-06-24', 'facility': 'C1', 'site': 'sgp', 'start_date': '2006-09-15'}]" + ] + }, + { + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "ac6764f5", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The following locations and date ranges are available for this VAP:\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
sitefacilitystart_dateend_date
0sgpC12006-09-152014-06-24
\n", + "
" + ], + "text/plain": [ + " site facility start_date end_date\n", + "0 sgp C1 2006-09-15 2014-06-24" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2014-06-22'\n", + "date_end = '2014-06-24'" + ] + }, + { + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'/data/archive/sgp/sgprlccnprof1ghanC1.c1'" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['20140622', '20140623', '20140624']" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "51feea2e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['/data/archive/sgp/sgprlccnprof1ghanC1.c1/sgprlccnprof1ghanC1.c1.20140624.000000.cdf']" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1 files loaded\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
<xarray.Dataset>\n",
+       "Dimensions:                           (time: 24, height: 52, ss_step: 7,\n",
+       "                                       param2: 2)\n",
+       "Coordinates:\n",
+       "  * time                              (time) datetime64[ns] 2014-06-24 ... 20...\n",
+       "  * height                            (height) float32 0.15 0.225 ... 3.9 3.975\n",
+       "  * ss_step                           (ss_step) float32 1.0 2.0 3.0 ... 6.0 7.0\n",
+       "Dimensions without coordinates: param2\n",
+       "Data variables: (12/62)\n",
+       "    base_time                         datetime64[ns] 2014-06-24\n",
+       "    time_offset                       (time) datetime64[ns] 2014-06-24 ... 20...\n",
+       "    qc_time                           (time) int32 dask.array<chunksize=(24,), meta=np.ndarray>\n",
+       "    rh_mean                           (time, height) float32 dask.array<chunksize=(24, 52), meta=np.ndarray>\n",
+       "    qc_rh_mean                        (time, height) int32 dask.array<chunksize=(24, 52), meta=np.ndarray>\n",
+       "    rh_std_dev                        (time, height) float32 dask.array<chunksize=(24, 52), meta=np.ndarray>\n",
+       "    ...                                ...\n",
+       "    qc_N_CCN_7                        (time) int32 dask.array<chunksize=(24,), meta=np.ndarray>\n",
+       "    temperature_second_deriv          (time, height) float32 dask.array<chunksize=(24, 52), meta=np.ndarray>\n",
+       "    cbh                               (time) float32 dask.array<chunksize=(24,), meta=np.ndarray>\n",
+       "    lat                               float32 ...\n",
+       "    lon                               float32 ...\n",
+       "    alt                               float32 ...\n",
+       "Attributes: (12/17)\n",
+       "    command_line:                   ccnprof -s sgp -f C1 -b 20140624 -e 20140...\n",
+       "    process_version:                v1.2\n",
+       "    dod_version:                    rlccnprof1ghan-c1-0.5\n",
+       "    site_id:                        sgp\n",
+       "    facility_id:                    C1: Lamont, Oklahoma\n",
+       "    input_datastreams:              sgpaosccn100C1.a1 : 12.9 : 20140624.00000...\n",
+       "    ...                             ...\n",
+       "    history:                        created by user dsmgr on machine iron at ...\n",
+       "    _file_dates:                    ['20140624']\n",
+       "    _file_times:                    ['000000']\n",
+       "    datastream:                     \n",
+       "    _datastream:                    \n",
+       "    _arm_standards_flag:            1
" + ], + "text/plain": [ + "\n", + "Dimensions: (time: 24, height: 52, ss_step: 7,\n", + " param2: 2)\n", + "Coordinates:\n", + " * time (time) datetime64[ns] 2014-06-24 ... 20...\n", + " * height (height) float32 0.15 0.225 ... 3.9 3.975\n", + " * ss_step (ss_step) float32 1.0 2.0 3.0 ... 6.0 7.0\n", + "Dimensions without coordinates: param2\n", + "Data variables: (12/62)\n", + " base_time datetime64[ns] 2014-06-24\n", + " time_offset (time) datetime64[ns] 2014-06-24 ... 20...\n", + " qc_time (time) int32 dask.array\n", + " rh_mean (time, height) float32 dask.array\n", + " qc_rh_mean (time, height) int32 dask.array\n", + " rh_std_dev (time, height) float32 dask.array\n", + " ... ...\n", + " qc_N_CCN_7 (time) int32 dask.array\n", + " temperature_second_deriv (time, height) float32 dask.array\n", + " cbh (time) float32 dask.array\n", + " lat float32 ...\n", + " lon float32 ...\n", + " alt float32 ...\n", + "Attributes: (12/17)\n", + " command_line: ccnprof -s sgp -f C1 -b 20140624 -e 20140...\n", + " process_version: v1.2\n", + " dod_version: rlccnprof1ghan-c1-0.5\n", + " site_id: sgp\n", + " facility_id: C1: Lamont, Oklahoma\n", + " input_datastreams: sgpaosccn100C1.a1 : 12.9 : 20140624.00000...\n", + " ... ...\n", + " history: created by user dsmgr on machine iron at ...\n", + " _file_dates: ['20140624']\n", + " _file_times: ['000000']\n", + " datastream: \n", + " _datastream: \n", + " _arm_standards_flag: 1" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['nsteps', 'rh_mean', 'rh_std_dev']" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [ + { + "ename": "KeyError", + "evalue": "'nsteps'", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mKeyError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[0;32mIn[9], line 5\u001b[0m\n\u001b[1;32m 2\u001b[0m ts_display\u001b[38;5;241m.\u001b[39madd_subplots((\u001b[38;5;28mlen\u001b[39m(variables_to_plot),), figsize \u001b[38;5;241m=\u001b[39m (\u001b[38;5;241m9.5\u001b[39m,\u001b[38;5;241m4\u001b[39m\u001b[38;5;241m*\u001b[39m\u001b[38;5;28mlen\u001b[39m(variables_to_plot)))\n\u001b[1;32m 4\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m i,v \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28menumerate\u001b[39m(variables_to_plot):\n\u001b[0;32m----> 5\u001b[0m ts_ax \u001b[38;5;241m=\u001b[39m ts_display\u001b[38;5;241m.\u001b[39mplot(v, subplot_index\u001b[38;5;241m=\u001b[39m(i,), set_title\u001b[38;5;241m=\u001b[39m\u001b[43mds\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mvariables\u001b[49m\u001b[43m[\u001b[49m\u001b[43mv\u001b[49m\u001b[43m]\u001b[49m\u001b[38;5;241m.\u001b[39mattrs[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mlong_name\u001b[39m\u001b[38;5;124m'\u001b[39m],)\n\u001b[1;32m 6\u001b[0m ts_ax\u001b[38;5;241m.\u001b[39mgrid()\n\u001b[1;32m 8\u001b[0m plt\u001b[38;5;241m.\u001b[39mshow()\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/core/utils.py:453\u001b[0m, in \u001b[0;36mFrozen.__getitem__\u001b[0;34m(self, key)\u001b[0m\n\u001b[1;32m 452\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21m__getitem__\u001b[39m(\u001b[38;5;28mself\u001b[39m, key: K) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m V:\n\u001b[0;32m--> 453\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mmapping\u001b[49m\u001b[43m[\u001b[49m\u001b[43mkey\u001b[49m\u001b[43m]\u001b[49m\n", + "\u001b[0;31mKeyError\u001b[0m: 'nsteps'" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "b9705370d4b8484a87f6a3e53aec927d", + "version_major": 2, + "version_minor": 0 + }, + "image/png": "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1tnZmUVE9thjj2WdnZ3ZW2+9lWWZ6+/JJmwT9uSTT2aVlZVZYWFhNn/+/Gznzp3D/+zuu+/ObrrpphHH/+d//mf2J3/yJ1lhYWH2uc99Ltu8eXOOJ2aqGs9euummm7KIGPW4++67cz84U854f1/6XcKW3zXevXTw4MHs1ltvzS677LJs9uzZWXNzc/bBBx/keGqmqvHup8cffzz74he/mF122WVZeXl59ld/9VfZ0aNHczw1U81//Md/fOw1kOvvyZWXZd5TAQAAQLp8xhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAnbCbZr165YtmxZzJo1K/Ly8uKVV175xHN27twZ1dXVUVxcHNddd1089dRTEz8oAABAooTtBHv//fdj3rx58cQTT5zT8YcPH46lS5dGXV1ddHZ2xkMPPRSrVq2KF198cYInBQAASFNelmXZZA9xqcjLy4uXX345li9fftZjvv3tb8err74aBw8eHF5ramqKn/3sZ7Fv374cTAkAAJCWgskegJH27dsX9fX1I9Zuv/322LJlS3z44Ycxffr0Mc8bGhqKoaGh4eenT5+Od999N2bMmBF5eXkTOjMAAFzqsiyL48ePx6xZs2LaNG+MzTVhO8X09vZGWVnZiLWysrI4efJk9PX1RXl5+ZjntbS0xIYNG3IxIgAAcBZHjhyJ2bNnT/YYlxxhOwX9/h3WM+8W/7g7r+vWrYvm5ubh5wMDA3HttdfGkSNHoqSkZGIGBQAAIiJicHAwKioq4g//8A8ne5RLkrCdYq6++uro7e0dsXbs2LEoKCiIGTNmnPW8oqKiKCoqGrVeUlIibAEAIEd8DHByePP3FLNo0aJob28fsbZjx46oqak56+drAQAALmXCdoK999570dXVFV1dXRHx0df5dHV1RXd3d0R89BbixsbG4eObmprirbfeiubm5jh48GBs3bo1tmzZEg888MBkjA8AADDleSvyBNu/f3/cfPPNw8/PfA727rvvjm3btkVPT89w5EZEVFVVRVtbW6xZsyaefPLJmDVrVjz++OPxta99LeezAwAApMD32F6kBgcHo7S0NAYGBnzGFgAAJpjr78nlrcgAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKEbY5s2rQpqqqqori4OKqrq2P37t0fe/z27dtj3rx5cfnll0d5eXnce++90d/fn6NpAQAA0iFsc6C1tTVWr14d69evj87Ozqirq4slS5ZEd3f3mMfv2bMnGhsbY+XKlfHGG2/E888/Hz/96U/jvvvuy/HkAAAAU5+wzYHHHnssVq5cGffdd1/MmTMn/umf/ikqKipi8+bNYx7/X//1X/G5z30uVq1aFVVVVfGnf/qn8Y1vfCP279+f48kBAACmPmE7wU6cOBEdHR1RX18/Yr2+vj727t075jm1tbVx9OjRaGtriyzL4p133okXXngh7rjjjrP+nKGhoRgcHBzxAAAAuBQI2wnW19cXp06dirKyshHrZWVl0dvbO+Y5tbW1sX379mhoaIjCwsK4+uqr4zOf+Ux8//vfP+vPaWlpidLS0uFHRUXFBX0dAAAAU5WwzZG8vLwRz7MsG7V2xoEDB2LVqlXx8MMPR0dHR7z22mtx+PDhaGpqOuuvv27duhgYGBh+HDly5ILODwAAMFUVTPYAF7uZM2dGfn7+qLuzx44dG3UX94yWlpZYvHhxPPjggxER8aUvfSmuuOKKqKuri0cffTTKy8tHnVNUVBRFRUUX/gUAAABMce7YTrDCwsKorq6O9vb2Eevt7e1RW1s75jkffPBBTJs28v+a/Pz8iPjoTi8AAAD/R9jmQHNzczz99NOxdevWOHjwYKxZsya6u7uH31q8bt26aGxsHD5+2bJl8dJLL8XmzZvj0KFD8frrr8eqVatiwYIFMWvWrMl6GQAAAFOStyLnQENDQ/T398fGjRujp6cn5s6dG21tbVFZWRkRET09PSO+0/aee+6J48ePxxNPPBF/+7d/G5/5zGfilltuiX/4h3+YrJcAAAAwZeVl3tt6URocHIzS0tIYGBiIkpKSyR4HAAAuaq6/J5e3IgMAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2ObJp06aoqqqK4uLiqK6ujt27d3/s8UNDQ7F+/fqorKyMoqKi+PznPx9bt27N0bQAAADpKJjsAS4Fra2tsXr16ti0aVMsXrw4fvCDH8SSJUviwIEDce211455zooVK+Kdd96JLVu2xB/90R/FsWPH4uTJkzmeHAAAYOrLy7Ism+whLnYLFy6M+fPnx+bNm4fX5syZE8uXL4+WlpZRx7/22mvx9a9/PQ4dOhRXXnnlef3MwcHBKC0tjYGBgSgpKTnv2QEAgE/m+ntyeSvyBDtx4kR0dHREfX39iPX6+vrYu3fvmOe8+uqrUVNTE9/97nfjmmuuiRtuuCEeeOCB+M1vfpOLkQEAAJLircgTrK+vL06dOhVlZWUj1svKyqK3t3fMcw4dOhR79uyJ4uLiePnll6Ovry/uv//+ePfdd8/6OduhoaEYGhoafj44OHjhXgQAAMAU5o5tjuTl5Y14nmXZqLUzTp8+HXl5ebF9+/ZYsGBBLF26NB577LHYtm3bWe/atrS0RGlp6fCjoqLigr8GAACAqUjYTrCZM2dGfn7+qLuzx44dG3UX94zy8vK45pprorS0dHhtzpw5kWVZHD16dMxz1q1bFwMDA8OPI0eOXLgXAQAAMIUJ2wlWWFgY1dXV0d7ePmK9vb09amtrxzxn8eLF8fbbb8d77703vPaLX/wipk2bFrNnzx7znKKioigpKRnxAAAAuBQI2xxobm6Op59+OrZu3RoHDx6MNWvWRHd3dzQ1NUXER3dbGxsbh4+/8847Y8aMGXHvvffGgQMHYteuXfHggw/GX//1X8dll102WS8DAABgSvKXR+VAQ0ND9Pf3x8aNG6Onpyfmzp0bbW1tUVlZGRERPT090d3dPXz8H/zBH0R7e3v8zd/8TdTU1MSMGTNixYoV8eijj07WSwAAAJiyfI/tRcr3aAEAQO64/p5c3ooMAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YZsjmzZtiqqqqiguLo7q6urYvXv3OZ33+uuvR0FBQXz5y1+e2AEBAAASJWxzoLW1NVavXh3r16+Pzs7OqKuriyVLlkR3d/fHnjcwMBCNjY3x1a9+NUeTAgAApCcvy7Jssoe42C1cuDDmz58fmzdvHl6bM2dOLF++PFpaWs563te//vW4/vrrIz8/P1555ZXo6uo65585ODgYpaWlMTAwECUlJZ9mfAAA4BO4/p5c7thOsBMnTkRHR0fU19ePWK+vr4+9e/ee9bxnnnkm3nzzzXjkkUfO6ecMDQ3F4ODgiAcAAMClQNhOsL6+vjh16lSUlZWNWC8rK4ve3t4xz/nlL38Za9euje3bt0dBQcE5/ZyWlpYoLS0dflRUVHzq2QEAAFIgbHMkLy9vxPMsy0atRUScOnUq7rzzztiwYUPccMMN5/zrr1u3LgYGBoYfR44c+dQzAwAApODcbgdy3mbOnBn5+fmj7s4eO3Zs1F3ciIjjx4/H/v37o7OzM771rW9FRMTp06cjy7IoKCiIHTt2xC233DLqvKKioigqKpqYFwEAADCFuWM7wQoLC6O6ujra29tHrLe3t0dtbe2o40tKSuLnP/95dHV1DT+ampriC1/4QnR1dcXChQtzNToAAEAS3LHNgebm5rjrrruipqYmFi1aFD/84Q+ju7s7mpqaIuKjtxH/6le/ih/96Ecxbdq0mDt37ojzr7rqqiguLh61DgAAgLDNiYaGhujv74+NGzdGT09PzJ07N9ra2qKysjIiInp6ej7xO20BAAAYm++xvUj5Hi0AAMgd19+Ty2dsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCdsc2bRpU1RVVUVxcXFUV1fH7t27z3rsSy+9FLfddlt89rOfjZKSkli0aFH85Cc/yeG0AAAA6RC2OdDa2hqrV6+O9evXR2dnZ9TV1cWSJUuiu7t7zON37doVt912W7S1tUVHR0fcfPPNsWzZsujs7Mzx5AAAAFNfXpZl2WQPcbFbuHBhzJ8/PzZv3jy8NmfOnFi+fHm0tLSc06/xx3/8x9HQ0BAPP/zwOR0/ODgYpaWlMTAwECUlJec1NwAAcG5cf08ud2wn2IkTJ6KjoyPq6+tHrNfX18fevXvP6dc4ffp0HD9+PK688sqJGBEAACBpBZM9wMWur68vTp06FWVlZSPWy8rKore395x+je9973vx/vvvx4oVK856zNDQUAwNDQ0/HxwcPL+BAQAAEuOObY7k5eWNeJ5l2ai1sTz33HPxne98J1pbW+Oqq64663EtLS1RWlo6/KioqPjUMwMAAKRA2E6wmTNnRn5+/qi7s8eOHRt1F/f3tba2xsqVK+Nf/uVf4tZbb/3YY9etWxcDAwPDjyNHjnzq2QEAAFIgbCdYYWFhVFdXR3t7+4j19vb2qK2tPet5zz33XNxzzz3x7LPPxh133PGJP6eoqChKSkpGPAAAAC4FPmObA83NzXHXXXdFTU1NLFq0KH74wx9Gd3d3NDU1RcRHd1t/9atfxY9+9KOI+ChqGxsb45//+Z/jK1/5yvDd3ssuuyxKS0sn7XUAAABMRcI2BxoaGqK/vz82btwYPT09MXfu3Ghra4vKysqIiOjp6RnxnbY/+MEP4uTJk/HNb34zvvnNbw6v33333bFt27Zcjw8AADCl+R7bi5Tv0QIAgNxx/T25fMYWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasM2RTZs2RVVVVRQXF0d1dXXs3r37Y4/fuXNnVFdXR3FxcVx33XXx1FNP5WhSAACAtAjbHGhtbY3Vq1fH+vXro7OzM+rq6mLJkiXR3d095vGHDx+OpUuXRl1dXXR2dsZDDz0Uq1atihdffDHHkwMAAEx9eVmWZZM9xMVu4cKFMX/+/Ni8efPw2pw5c2L58uXR0tIy6vhvf/vb8eqrr8bBgweH15qamuJnP/tZ7Nu375x+5uDgYJSWlsbAwECUlJR8+hcBAACclevvyeWO7QQ7ceJEdHR0RH19/Yj1+vr62Lt375jn7Nu3b9Txt99+e+zfvz8+/PDDCZsVAAAgRQWTPcDFrq+vL06dOhVlZWUj1svKyqK3t3fMc3p7e8c8/uTJk9HX1xfl5eWjzhkaGoqhoaHh5wMDAxHx0Z8cAQAAE+vMdbc3xE4OYZsjeXl5I55nWTZq7ZOOH2v9jJaWltiwYcOo9YqKivGOCgAAnKf+/v4oLS2d7DEuOcJ2gs2cOTPy8/NH3Z09duzYqLuyZ1x99dVjHl9QUBAzZswY85x169ZFc3Pz8PNf//rXUVlZGd3d3f7F4lMZHByMioqKOHLkiM+L8KnYS1xI9hMXir3EhTIwMBDXXnttXHnllZM9yiVJ2E6wwsLCqK6ujvb29viLv/iL4fX29vb48z//8zHPWbRoUfzrv/7riLUdO3ZETU1NTJ8+fcxzioqKoqioaNR6aWmp36S5IEpKSuwlLgh7iQvJfuJCsZe4UKZN89cYTQb/q+dAc3NzPP3007F169Y4ePBgrFmzJrq7u6OpqSkiPrrb2tjYOHx8U1NTvPXWW9Hc3BwHDx6MrVu3xpYtW+KBBx6YrJcAAAAwZbljmwMNDQ3R398fGzdujJ6enpg7d260tbVFZWVlRET09PSM+E7bqqqqaGtrizVr1sSTTz4Zs2bNiscffzy+9rWvTdZLAAAAmLKEbY7cf//9cf/994/5z7Zt2zZq7aabbor//u//Pu+fV1RUFI888siYb0+G8bCXuFDsJS4k+4kLxV7iQrGXJlde5u+jBgAAIGE+YwsAAEDShC0AAABJE7YAAAAkTdgCAACQNGGbsE2bNkVVVVUUFxdHdXV17N69+2OP37lzZ1RXV0dxcXFcd9118dRTT+VoUqa68eyll156KW677bb47Gc/GyUlJbFo0aL4yU9+ksNpmcrG+/vSGa+//noUFBTEl7/85YkdkGSMdy8NDQ3F+vXro7KyMoqKiuLzn/98bN26NUfTMtWNdz9t37495s2bF5dffnmUl5fHvffeG/39/Tmalqlq165dsWzZspg1a1bk5eXFK6+88onnuP7OHWGbqNbW1li9enWsX78+Ojs7o66uLpYsWTLi+3B/1+HDh2Pp0qVRV1cXnZ2d8dBDD8WqVavixRdfzPHkTDXj3Uu7du2K2267Ldra2qKjoyNuvvnmWLZsWXR2duZ4cqaa8e6lMwYGBqKxsTG++tWv5mhSprrz2UsrVqyIf//3f48tW7bE//zP/8Rzzz0XN954Yw6nZqoa737as2dPNDY2xsqVK+ONN96I559/Pn7605/Gfffdl+PJmWref//9mDdvXjzxxBPndLzr7xzLSNKCBQuypqamEWs33nhjtnbt2jGP/7u/+7vsxhtvHLH2jW98I/vKV74yYTOShvHupbF88YtfzDZs2HChRyMx57uXGhoasr//+7/PHnnkkWzevHkTOCGpGO9e+rd/+7estLQ06+/vz8V4JGa8++kf//Efs+uuu27E2uOPP57Nnj17wmYkPRGRvfzyyx97jOvv3HLHNkEnTpyIjo6OqK+vH7FeX18fe/fuHfOcffv2jTr+9ttvj/3798eHH344YbMytZ3PXvp9p0+fjuPHj8eVV145ESOSiPPdS88880y8+eab8cgjj0z0iCTifPbSq6++GjU1NfHd7343rrnmmrjhhhvigQceiN/85je5GJkp7Hz2U21tbRw9ejTa2toiy7J455134oUXXog77rgjFyNzEXH9nVsFkz0A49fX1xenTp2KsrKyEetlZWXR29s75jm9vb1jHn/y5Mno6+uL8vLyCZuXqet89tLv+973vhfvv/9+rFixYiJGJBHns5d++ctfxtq1a2P37t1RUOA/R3zkfPbSoUOHYs+ePVFcXBwvv/xy9PX1xf333x/vvvuuz9le4s5nP9XW1sb27dujoaEhfvvb38bJkyfjz/7sz+L73/9+LkbmIuL6O7fcsU1YXl7eiOdZlo1a+6Tjx1rn0jPevXTGc889F9/5zneitbU1rrrqqokaj4Sc6146depU3HnnnbFhw4a44YYbcjUeCRnP70unT5+OvLy82L59eyxYsCCWLl0ajz32WGzbts1dWyJifPvpwIEDsWrVqnj44Yejo6MjXnvttTh8+HA0NTXlYlQuMq6/c8cfkSdo5syZkZ+fP+pPGo8dOzbqT4XOuPrqq8c8vqCgIGbMmDFhszK1nc9eOqO1tTVWrlwZzz//fNx6660TOSYJGO9eOn78eOzfvz86OzvjW9/6VkR8FCdZlkVBQUHs2LEjbrnllpzMztRyPr8vlZeXxzXXXBOlpaXDa3PmzIksy+Lo0aNx/fXXT+jMTF3ns59aWlpi8eLF8eCDD0ZExJe+9KW44ooroq6uLh599FF32Thnrr9zyx3bBBUWFkZ1dXW0t7ePWG9vb4/a2toxz1m0aNGo43fs2BE1NTUxffr0CZuVqe189lLER3dq77nnnnj22Wd95oiIGP9eKikpiZ///OfR1dU1/GhqaoovfOEL0dXVFQsXLszV6Ewx5/P70uLFi+Ptt9+O9957b3jtF7/4RUybNi1mz549ofMytZ3Pfvrggw9i2rSRl8j5+fkR8X932+BcuP7OsUn6S6v4lH784x9n06dPz7Zs2ZIdOHAgW716dXbFFVdk//u//5tlWZatXbs2u+uuu4aPP3ToUHb55Zdna9asyQ4cOJBt2bIlmz59evbCCy9M1ktgihjvXnr22WezgoKC7Mknn8x6enqGH7/+9a8n6yUwRYx3L/0+fysyZ4x3Lx0/fjybPXt29pd/+ZfZG2+8ke3cuTO7/vrrs/vuu2+yXgJTyHj30zPPPJMVFBRkmzZtyt58881sz549WU1NTbZgwYLJeglMEcePH886Ozuzzs7OLCKyxx57LOvs7MzeeuutLMtcf082YZuwJ598MqusrMwKCwuz+fPnZzt37hz+Z3fffXd20003jTj+P//zP7M/+ZM/yQoLC7PPfe5z2ebNm3M8MVPVePbSTTfdlEXEqMfdd9+d+8GZcsb7+9LvErb8rvHupYMHD2a33nprdtlll2WzZ8/Ompubsw8++CDHUzNVjXc/Pf7449kXv/jF7LLLLsvKy8uzv/qrv8qOHj2a46mZav7jP/7jY6+BXH9Prrws854KAAAA0uUztgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YTbNeuXbFs2bKYNWtW5OXlxSuvvPKJ5+zcuTOqq6ujuLg4rrvuunjqqacmflAAAIBECdsJ9v7778e8efPiiSeeOKfjDx8+HEuXLo26urro7OyMhx56KFatWhUvvvjiBE8KAACQprwsy7LJHuJSkZeXFy+//HIsX778rMd8+9vfjldffTUOHjw4vNbU1BQ/+9nPYt++fTmYEgAAIC0Fkz0AI+3bty/q6+tHrN1+++2xZcuW+PDDD2P69Oljnjc0NBRDQ0PDz0+fPh3vvvtuzJgxI/Ly8iZ0ZgAAuNRlWRbHjx+PWbNmxbRp3hiba8J2iunt7Y2ysrIRa2VlZXHy5Mno6+uL8vLyMc9raWmJDRs25GJEAADgLI4cORKzZ8+e7DEuOcJ2Cvr9O6xn3i3+cXde161bF83NzcPPBwYG4tprr40jR45ESUnJxAwKAABERMTg4GBUVFTEH/7hH072KJckYTvFXH311dHb2zti7dixY1FQUBAzZsw463lFRUVRVFQ0ar2kpETYAgBAjvgY4OTw5u8pZtGiRdHe3j5ibceOHVFTU3PWz9cCAABcyoTtBHvvvfeiq6srurq6IuKjr/Pp6uqK7u7uiPjoLcSNjY3Dxzc1NcVbb70Vzc3NcfDgwdi6dWts2bIlHnjggckYHwAAYMrzVuQJtn///rj55puHn5/5HOzdd98d27Zti56enuHIjYioqqqKtra2WLNmTTz55JMxa9asePzxx+NrX/tazmcHAABIge+xvUgNDg5GaWlpDAwM+IwtAABMMNffk8tbkQEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAnbHNm0aVNUVVVFcXFxVFdXx+7duz/2+O3bt8e8efPi8ssvj/Ly8rj33nujv78/R9MCAACkQ9jmQGtra6xevTrWr18fnZ2dUVdXF0uWLInu7u4xj9+zZ080NjbGypUr44033ojnn38+fvrTn8Z9992X48kBAACmPmGbA4899lisXLky7rvvvpgzZ0780z/9U1RUVMTmzZvHPP6//uu/4nOf+1ysWrUqqqqq4k//9E/jG9/4Ruzfvz/HkwMAAEx9wnaCnThxIjo6OqK+vn7Een19fezdu3fMc2pra+Po0aPR1tYWWZbFO++8Ey+88ELccccduRgZAAAgKcJ2gvX19cWpU6eirKxsxHpZWVn09vaOeU5tbW1s3749GhoaorCwMK6++ur4zGc+E9///vfP+nOGhoZicHBwxAMAAOBSIGxzJC8vb8TzLMtGrZ1x4MCBWLVqVTz88MPR0dERr732Whw+fDiamprO+uu3tLREaWnp8KOiouKCzg8AADBV5WVZlk32EBezEydOxOWXXx7PP/98/MVf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yZlq3n///Zg3b1488cQT53S86+8cy0jSggULsqamphFrN954Y7Z27doxj/+7v/u77MYbbxyx9o1vfCP7yle+MmEzkobx7qWxfPGLX8w2bNhwoUcjMee7lxoaGrK///u/zx555JFs3rx5EzghqRjvXvq3f/u3rLS0NOvv78/FeCRmvPvpH//xH7PrrrtuxNrjjz+ezZ49e8JmJD0Rkb388ssfe4zr79xyxzZBJ06ciI6Ojqivrx+xXl9fH3v37h3znH379o06/vbbb4/9+/fHhx9+OGGzMrWdz176fadPn47jx4/HlVdeOREjkojz3UvPPPNMvPnmm/HII49M9Igk4nz20quvvho1NTXx3e9+N6655pq44YYb4oEHHojf/OY3uRiZKex89lNtbW0cPXo02traIsuyeOedd+KFF16IO+64IxcjcxFx/Z1bBZM9AOPX19cXp06dirKyshHrZWVl0dvbO+Y5vb29Yx5/8uTJ6Ovri/Ly8gmbl6nrfPbS7/ve974X77//fqxYsWIiRiQR57OXfvnLX8batWtj9+7dUVDgP0d85Hz20qFDh2LPnj1RXFwcL7/8cvT19cX9998f7777rs/ZXuLOZz/V1tbG9u3bo6GhIX7729/GyZMn48/+7M/i+9//fi5G5iLi+ju33LFNWF5e3ojnWZaNWvuk48da59Iz3r10xnPPPRff+c53orW1Na666qqJGo+EnOteOnXqVNx5552xYcOGuOGGG3I1HgkZz+9Lp0+fjry8vNi+fXssWLAgli5dGo899lhs27bNXVsiYnz76cCBA7Fq1ap4+OGHo6OjI1577bU4fPhwNDU15WJULjKuv3PHH5EnaObMmZGfnz/qTxqPHTs26k+Fzrj66qvHPL6goCBmzJgxYbMytZ3PXjqjtbU1Vq5cGc8//3zceuutEzkmCRjvXjp+/Hjs378/Ojs741vf+lZEfBQnWZZFQUFB7NixI2655ZaczM7Ucj6/L5WXl8c111wTpaWlw2tz5syJLMvi6NGjcf3110/ozExd57OfWlpaYvHixfHggw9GRMSXvvSluOKKK6Kuri4effRRd9k4Z66/c8sd2wQVFhZGdXV1tLe3j1hvb2+P2traMc9ZtGjRqON37NgRNTU1MX369AmblantfPZSxEd3au+555549tlnfeaIiBj/XiopKYmf//zn0dXVNfxoamqKL3zhC9HV1RULFy7M1ehMMefz+9LixYvj7bffjvfee2947Re/+EVMmzYtZs+ePaHzMrWdz3764IMPYtq0kZfI+fn5EfF/d9vgXLj+zrFJ+kur+JR+/OMfZ9OnT8+2bNmSHThwIFu9enV2xRVXZP/7v/+bZVmWrV27NrvrrruGjz906FB2+eWXZ2vWrMkOHDiQbdmyJZs+fXr2wgsvTNZLYIoY71569tlns4KCguzJJ5/Menp6hh+//vWvJ+slMEWMdy/9Pn8rMmeMdy8dP348mz17dvaXf/mX2RtvvJHt3Lkzu/7667P77rtvsl4CU8h499MzzzyTFRQUZJs2bcrefPPNbM+ePVlNTU22YMGCyXoJTBHHjx/POjs7s87Oziwissceeyzr7OzM3nrrrSzLXH9PNmGbsCeffDKrrKzMCgsLs/nz52c7d+4c/md33313dtNNN404/j//8z+zP/mTP8kKCwuzz33uc9nmzZtzPDFT1Xj20k033ZRFxKjH3XffnfvBmXLG+/vS7xK2/K7x7qWDBw9mt956a3bZZZdls2fPzpqbm7MPPvggx1MzVY13Pz3++OPZF7/4xeyyyy7LysvLs7/6q7/Kjh49muOpmWr+4z/+42OvgVx/T668LPOeCgAAANLlM7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQtP8PDcrXAZbhxkUAAAAASUVORK5CYII=", + "text/html": [ + "\n", + "
\n", + "
\n", + " Figure\n", + "
\n", + " \n", + "
\n", + " " + ], + "text/plain": [ + "Canvas(toolbar=Toolbar(toolitems=[('Home', 'Reset original view', 'home', 'home'), ('Back', 'Back to previous …" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'rh_mean'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'nsteps'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/CCNPROF/CCNPROF_tutorial.ipynb b/VAPs/quicklook/CCNPROF/CCNPROF_tutorial.ipynb new file mode 100644 index 00000000..4a92854b --- /dev/null +++ b/VAPs/quicklook/CCNPROF/CCNPROF_tutorial.ipynb @@ -0,0 +1,838 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# RLCCNPROF1GHAN.C1 Notebook\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/ccnprof) for more information about this vap." + ] + }, + { + "cell_type": "markdown", + "id": "97097763", + "metadata": {}, + "source": [ + "In this notebook, we demonstrate the workflow to explore ARM vap data (using rlccnprof1ghan as an example.) Value-added products (VAPs) are higher-order data products that have been analyzed and processed to ease scientists’ use of ARM data in atmospheric research and global climate models. \n", + "Here is the main content we will cover." + ] + }, + { + "cell_type": "markdown", + "id": "eddec40f", + "metadata": {}, + "source": [ + "# Table of Content\n", + "## Access the data\n", + "* How to retrieve the data\n", + "* Data path and file name conventions\n", + "* Load data\n", + "## Explore the data\n", + "* NetCDF Data structure\n", + "* Xarray essentials\n", + "* Xarray Variable\n", + "## Plot the data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "import random\n", + "\n", + "import glob\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "2372d5be", + "metadata": {}, + "source": [ + "## Access the data" + ] + }, + { + "cell_type": "markdown", + "id": "5b0d9684", + "metadata": {}, + "source": [ + "### How to retrieve the data\n", + "We assume the path \"/data/archive\" is available where you are running this notebook. You can use `os.path.exists(\"/data/archive\")` to verify if the path exists at your machine.\n", + "\n", + "\n", + "### Data path and file name conventions\n", + "There several common terminologies regarding ARM data, for example, data-stream-name, data-level, site, facility, instrument, etc. (For more details, please see [ARM Data File Standards Version 1.2](https://www.google.com/search?q=arm+datastream+facility+cite+definition&rlz=1C1GCEJ_enUS1029US1029&ei=hb41ZICrBPukqtsPvMODIA&ved=0ahUKEwjAgeiS0aL-AhV7kmoFHbzhAAQQ4dUDCBA&uact=5&oq=arm+datastream+facility+cite+definition&gs_lcp=Cgxnd3Mtd2l6LXNlcnAQAzIFCAAQogQyBQgAEKIEMgUIABCiBDIFCAAQogQyBQgAEKIEOgoIABBHENYEELADOgoIIRCgARDDBBAKSgQIQRgAUJ4DWIoOYO0PaAFwAXgAgAGMAYgBuQmSAQMyLjmYAQCgAQHIAQjAAQE&sclient=gws-wiz-serp).) \n", + "\n", + "For example, this notebook is called `rlccnprof1ghan.c1`, where `rlccnprof1ghan` is the \"datastream name\", and the `{process.ds_class_level}}` is the \"data level\".\n", + "\n", + "This datastream also contains site `sgp` and facility `C1`. (Note: individual datastream might have multiple site-facility pairs.)\n", + "In such a case, the data of this data-stream is stored at `/data/archive/sgp/sgprlccnprof1ghanC1.c1`, which is in the format of `//.`. We can use the following method to assign the data-stream directory `datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )`\n", + "\n", + "The data files under datastream_dir also follows naming conventions. But once reach the datastream_dir level, the most import file naming convention to differentiate the files is \"yyyyMMdd.hhmmss\", which comes handy to filter out files based on datetime. For example, we can use `glob.glob(f'{datastream_dir}/*.200709*.*')` to filter files in 2007 September.\n", + "\n", + "(Note: refer to the https://adc.arm.gov/discovery/#/ and https://adc.arm.gov/solr8/metadata/select API to explore ARM datastream and assoicated available sites and facilities\n", + "\n", + "Please see the following examples in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d7e9eb85", + "metadata": {}, + "outputs": [], + "source": [ + "# Verify if DATA_DIR path exists\n", + "DATA_DIR = \"/data/archive\"\n", + "os.path.exists(DATA_DIR)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "586993fd", + "metadata": {}, + "outputs": [], + "source": [ + "# Speicify datastream_dir following the path conventions and check its existence\n", + "DATASTREAM_NAME = \"rlccnprof1ghan\"\n", + "DATA_LEVEL = \"c1\"\n", + "site = \"sgp\"\n", + "facility = \"C1\"\n", + "datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )\n", + "print(datastream_dir)\n", + "print(os.path.exists(datastream_dir))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0742f7c1", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: list 5 (random) files under datastream_dir\n", + "files = os.listdir(datastream_dir)\n", + "files[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39b98a36", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: get most recent file\n", + "list_of_files = glob.glob(f\"{datastream_dir}/*\") # * means all if need specific format then *.csv\n", + "latest_file = max(list_of_files, key=os.path.getctime)\n", + "latest_file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "902d514e", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus sort datastream files based on datetime\n", + "files = os.listdir(datastream_dir)\n", + "file_sorted = files.copy()\n", + "file_sorted.sort() \n", + "print(file_sorted[:5])\n", + "\n", + "# to reverse\n", + "file_sorted_reverse = files.copy() \n", + "file_sorted_reverse.sort(reverse=True)\n", + "print(file_sorted_reverse[:5])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec5923b2", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: pattern matching\n", + "# filter the 200709** files under datastream_dir\n", + "files_filter = glob.glob(f'{datastream_dir}/*.200709*.*')\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c4a4aa18", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: use armnotebook_utils.file_filter (TODO) to filter files based on datastream info \n" + ] + }, + { + "cell_type": "markdown", + "id": "cfeb9efc", + "metadata": {}, + "source": [ + "### Load data\n", + "The arm data is stored in [NetCDF](https://en.wikipedia.org/wiki/NetCDF#:~:text=%22NetCDF%20(network%20Common%20Data%20Form,format%20for%20representing%20scientific%20data.) format. We can use xarray's `open_dataset` method to load single file, or `open_mfdataset` to open multiple files. (Note: the latter will still return a single xarray dataset object by combining multiple files.)\n", + "\n", + "Note: open_dataset keeps the file handle open and lazy loads its contents. All parameters are passed directly to open_dataset. It is a preferable method over load_dataset for memory-effiency. (more details, please see [xarray.open_dataset](https://docs.xarray.dev/en/stable/generated/xarray.open_dataset.html))\n", + "\n", + "See the following example in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a440a329", + "metadata": {}, + "outputs": [], + "source": [ + "# open single file\n", + "full_path = latest_file\n", + "print(full_path)\n", + "ds_single = xr.open_dataset(full_path)\n", + "ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0143a3d", + "metadata": {}, + "outputs": [], + "source": [ + "print(ds_single)\n", + "print(type(ds_single))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c0f8939", + "metadata": {}, + "outputs": [], + "source": [ + "# open multiple files\n", + "n_files = 3\n", + "full_paths = [os.path.join(datastream_dir, f_path) for f_path in file_sorted_reverse[:n_files]]\n", + "print(full_paths)\n", + "try: # Note: sometimes multiple files cannot be merged, so we used try except here.\n", + " ds_mutiple = xr.open_mfdataset(full_paths)\n", + " # ds_mutiple\n", + " print(type(ds_mutiple))\n", + " print(ds_mutiple)\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "3fa59943", + "metadata": {}, + "source": [ + "## Explore the data" + ] + }, + { + "cell_type": "markdown", + "id": "571f69b9", + "metadata": {}, + "source": [ + "### NetCDF Data structure \n", + "\n", + "\n", + "(If you are confident with NetCDF basics and xarray essentials, feel free to skip this session.)\n", + "Before we dive into data exploration, there are some eseential concpets we should be familiar with (shown below): \n", + "* Dataset\n", + "* Data array\n", + "* Variable\n", + "* Dimenssion\n", + "* Coordinate\n", + "* Data Type\n", + "* Meta Data (Attributes)\n", + "\n", + "We will not go into details about NetCDF basics and here are some references you might find helpful\n", + "* [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#)\n", + "* [Network Common Data Form (NetCDF)](https://www.unidata.ucar.edu/software/netcdf/) and [A Brief History of (netCDF) Time](https://www.unidata.ucar.edu/software/netcdf/time/recs.html)\n", + "\n", + "\n", + "\n", + "### Xarray essentials\n", + "Earlier we introduced the xarray pacakge and used `open_dataset` and `open_mfdataset` to retrieve NetCDF data file as an xarray Dataset object. Recall that you can review the data in a notebook by using `print(ds)` or `ds`. \n", + "\n", + "Using xarray to retrieve the aforementioned NetCDF basics is straightforwared. In general,\n", + "* Dataset: `ds`\n", + "* Data array: `ds.variables`\n", + "* Variable: `ds.ds.variables`\n", + "* Dimenssion: `ds.dims`\n", + "* Coordinate: `ds.coords`\n", + "* Data Type: `type`\n", + "* Meta Data (Attributes)\n", + "\n", + "Also, here are some references if you are new to xarray\n", + "* [Xarray in 45 minutes](https://tutorial.xarray.dev/overview/xarray-in-45-min.html)\n", + "* [Handling NetCDF Files using XArray for Absolute Beginners](https://towardsdatascience.com/handling-netcdf-files-using-xarray-for-absolute-beginners-111a8ab4463f)\n", + "\n", + "Try the following commands in action\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b3eac323", + "metadata": {}, + "outputs": [], + "source": [ + "ds = ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77ecf85d", + "metadata": {}, + "outputs": [], + "source": [ + "# Dataset \n", + "ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25e7de09", + "metadata": {}, + "outputs": [], + "source": [ + "# Data array, Variable\n", + "# Note: the info can be overwhelming. Do not attempt to grasp everything at the first glance.\n", + "ds.variables" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c41b67e", + "metadata": {}, + "outputs": [], + "source": [ + "# dimenssions\n", + "ds.dims" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "156f1dfc", + "metadata": {}, + "outputs": [], + "source": [ + "# coordinates\n", + "ds.coords" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "277d6064", + "metadata": {}, + "outputs": [], + "source": [ + "# Meta Data (Attributes)\n", + "ds.attrs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d334681f", + "metadata": {}, + "outputs": [], + "source": [ + "# type\n", + "print(type(ds))\n", + "print(type(ds.variables))\n", + "print(type(ds.dims))\n", + "print(type(ds.coords))\n", + "print(type(ds.attrs))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "643399d6", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: retrieve variable names only\n", + "list(ds.variables)" + ] + }, + { + "cell_type": "markdown", + "id": "1cf36878", + "metadata": {}, + "source": [ + "#### Discussion: variable vs. coordinates vs. dimenssions. \n", + "From [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#):\n", + "\n", + "* Dimession: A dimension may be used to represent a real physical dimension, for example, time, latitude, longitude, or height. A dimension might also be used to index other quantities, for example station or model-run-number.\n", + "A netCDF dimension has both a name and a length. A dimension length is an arbitrary positive integer, except that one dimension in a netCDF dataset can have the length UNLIMITED.\n", + "\n", + "* Variables: Variables are used to store the bulk of the data in a netCDF dataset. A variable represents an array of values of the same type. A scalar value is treated as a 0-dimensional array. A variable has a name, a data type, and a shape described by its list of dimensions specified when the variable is created. A variable may also have associated attributes, which may be added, deleted or changed after the variable is created.\n", + "\n", + "* Coordinate (Variables): It is legal for a variable to have the same name as a dimension. Such variables have no special meaning to the netCDF library. However there is a convention that such variables should be treated in a special way by software using this library.\n", + "A variable with the same name as a dimension is called a coordinate variable. It typically defines a physical coordinate corresponding to that dimension.\n", + "\n", + "\n", + "Tips: By definition, coordinates and dimenssions are also variable. For example ds.time is a coordinate but it is also a special variable. But in practice, when we talk about variable, it implies regular/non-coordinate variable. We will use this convention for the remaining notebook. " + ] + }, + { + "cell_type": "markdown", + "id": "d47120f9", + "metadata": {}, + "source": [ + "### Xarray Variable\n", + "\n", + "\n", + "We can use `ds.variables` to access varialbes of a dataset. But many times viewing print-out the whole datasets can be overwhemling. Instead, we would work on the individual variable (also called Xarray data array.) using the `ds[var_name]` syntax, where `var_name` is the name of the variable. See the following examples in action.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5a57e136", + "metadata": {}, + "outputs": [], + "source": [ + "print(type(ds[\"time\"]))\n", + "ds[\"time\"]" + ] + }, + { + "cell_type": "markdown", + "id": "51ec643e", + "metadata": {}, + "source": [ + "#### Variable properties\n", + "For individual varible, the following properties are mostly used with the assocaited xarray method to retrieve them. (assuming var = ds[var_name])\n", + "* name: `var.name`\n", + "* data content: `var.data`\n", + "* attributes: `var.attrs`\n", + "* dimenstions: `var.dims`\n", + "* data type: `var.data.dtype`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a632a525", + "metadata": {}, + "outputs": [], + "source": [ + "var_name = \"time\"\n", + "var = ds[var_name]\n", + "\n", + "print(\"var.name: \\n\", var.name, \"\\n\")\n", + "print(\"var.data: \\n\", var.data, \"\\n\")\n", + "print(\"var.attrs: \\n\", var.attrs, \"\\n\")\n", + "print(\"var.dims: \\n\", var.dims, \"\\n\")\n", + "print(\"var.data.dtype: \\n\", var.data.dtype, \"\\n\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b256b07f", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: indivdual variable info table (collect and display variable info as a tabular format)\n", + "df_info = pd.DataFrame()\n", + "df_info[\"var_name\"] = list(ds.variables)\n", + "df_info[\"dims\"] = df_info.var_name.apply(lambda x: ds[x].dims)\n", + "df_info[\"is_dim\"] = df_info.var_name.apply(lambda x: x in ds.dims)\n", + "df_info[\"n_dim\"] = df_info.var_name.apply(lambda x: len(ds[x].dims))\n", + "df_info[\"attrs\"] = df_info.var_name.apply(lambda x: ds[x].attrs)\n", + "df_info[\"dtype\"] = df_info.var_name.apply(lambda x: ds[x].data.dtype)\n", + "df_info" + ] + }, + { + "cell_type": "markdown", + "id": "e6a36b96", + "metadata": {}, + "source": [ + "### Data cleaning/Preprocessing (skipped)\n", + "Data cleaning and preprocessing is an important stage in the the data analysis pipeline. However it will be out of the scope of this notebook. For more information about data cleaning and preprocessing basics, here are some references. \n", + "* [Xarray Fundamentals](https://earth-env-data-science.github.io/lectures/xarray/xarray.html)\n", + "* [Xarray Tutorial — Pangeo Gallery documentation](http://gallery.pangeo.io/repos/pangeo-data/pangeo-tutorial-gallery/xarray.html)\n", + "* [Pythonic Data Cleaning With pandas and NumPy](https://realpython.com/python-data-cleaning-numpy-pandas/)\n", + "* [Pandas - Cleaning Data](https://www.w3schools.com/python/pandas/pandas_cleaning.asp)" + ] + }, + { + "cell_type": "markdown", + "id": "ae6e1a69", + "metadata": {}, + "source": [ + "### Plotting\n", + "Data visualization (or plotting) is another important data analysis topic and deserves its own discussion. Here in this notebook we will only demonstrate basic tool for simple data visualization tasks.\n", + "\n", + "Here are some reference you might find useful:\n", + "* xarray plotting: https://docs.xarray.dev/en/stable/user-guide/plotting.html\n", + "* Atmospheric Community Toolkit (ACT): https://arm-doe.github.io/ACT/index.html\n", + "\n", + "Note: this notebook is auto-generated using a template. It uses a general idea to select variable(s) to plot and is not customized to each indivdual notebook. Feel free to change the variables in intrrests, especially certain figure is failed to plot." + ] + }, + { + "cell_type": "markdown", + "id": "7e417fb2", + "metadata": {}, + "source": [ + "#### 1-dimenssional basic time series plot\n", + "\n", + "For the following plot we would like to find variables such that\n", + "* it has one and only one dimession\n", + "* \"time\" is its coordinate variable\n", + "* it is not a dimenssion itself,\n", + "* it is not a special variable with substrings within [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "\n", + "Please see the following example in action. (For more details about pandas filtering, please see the following references.)\n", + "* [pandas: multiple conditions while indexing data frame](https://stackoverflow.com/questions/22591174/pandas-multiple-conditions-while-indexing-data-frame-unexpected-behavior)\n", + "* [pandas.DataFrame.apply](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.apply.html)\n", + "* [How to test if a string contains one of the substrings in a list...](https://stackoverflow.com/questions/26577516/how-to-test-if-a-string-contains-one-of-the-substrings-in-a-list-in-pandas" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30edac2d", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==1, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter = df_info[(df_info.n_dim == 1) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4777a659", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_1d = df_filter.var_name.values[0]\n", + " var_1d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb5ae985", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " ds[var_1d].plot()\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "9e41066e", + "metadata": {}, + "source": [ + "#### 2-dimenssional basic plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09bd4adc", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==2, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_2 = df_info[(df_info.n_dim == 2) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2cdea1a9", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_2d = df_filter_2.var_name.values[0]\n", + " var_2d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5f48879", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " print(ds[var_2d].dims)\n", + " # ds[var_2d].plot()\n", + "\n", + " # conventionally, use \"time\" as x-axis\n", + " ds[var_2d].plot(x=\"time\")\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "7554b2a6", + "metadata": {}, + "source": [ + "#### qc-plotting (optional)\n", + "\n", + "Note: act qc plotting has more strict requirement, one of them is the associated qc variable need to have \"flag_masks\" attributes. Which is added by using ds = act.io.armfiles.read_netcdf(files_list), then ds.clean.cleanup()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71c2096f", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " ds_act = act.io.armfiles.read_netcdf(full_path)\n", + " print(type(ds_act))\n", + " ds_act.clean.cleanup()\n", + "\n", + " # or \n", + " # ds.clean.cleanup()\n", + "except Exception as e:\n", + " print(\"ERROR\", e)\n", + " ds_act = ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "844f8505", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter valid variables for ACT qc plotting\n", + "condition = (\"qc_\" + df_info.var_name).apply(lambda x: ds_act[x].attrs.get(\"flag_masks\") is not None \n", + " if x in list(ds.variables) else False)\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_3 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings))) &\n", + " condition\n", + " ]\n", + "df_filter_3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd38cc6", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "try:\n", + " qc_variable = df_filter_3.var_name.values[0]\n", + " print(qc_variable)\n", + "except Exception as e:\n", + " print(e)\n", + " \n", + "try:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + " qc_ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "d76e4d27", + "metadata": {}, + "source": [ + "#### bonus: choose variables to plot from a dropdown menu " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb733804", + "metadata": {}, + "outputs": [], + "source": [ + "# Valid variables filtering\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_4 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82d5c20d", + "metadata": {}, + "outputs": [], + "source": [ + "# example 1: using xarray plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2fc9aaa4", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# fig, ax = plt.subplots(figsize=(10, 4))\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "# fig.clear() # Remove old lines from plot and plot new one\n", + "# if len(ds[var].dims)==2:\n", + "# ds[var].plot(x=\"time\", add_colorbar=False)\n", + "# else:\n", + "# ds[var].plot()\n", + "# plt.grid()\n", + "# plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fabdd802", + "metadata": {}, + "outputs": [], + "source": [ + "# example 2: using act plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "962b4186", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "\n", + "\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "\n", + "# i_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + "# i_display.add_subplots((1,), figsize=(10, 4))\n", + "# ax = i_display.plot(var, subplot_index=(0,), set_title=f\"{var} ({ds_act[var].attrs['long_name']})\",)\n", + "\n", + "# ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "# ax.grid()\n", + "# plt.show()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/CCNPROF/rlccnprof1ghan.c1.ipynb b/VAPs/quicklook/CCNPROF/rlccnprof1ghan.c1.ipynb new file mode 100644 index 00000000..78c85732 --- /dev/null +++ b/VAPs/quicklook/CCNPROF/rlccnprof1ghan.c1.ipynb @@ -0,0 +1,339 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# RLCCNPROF1GHAN.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/ccnprof) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'rlccnprof1ghan'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2014-06-24', 'facility': 'C1', 'site': 'sgp', 'start_date': '2006-09-15'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2014-06-22'\n", + "date_end = '2014-06-24'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['nsteps', 'rh_mean', 'rh_std_dev']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'rh_mean'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'nsteps'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/CLAP/CLAP_tutorial.ipynb b/VAPs/quicklook/CLAP/CLAP_tutorial.ipynb new file mode 100644 index 00000000..9ef362ce --- /dev/null +++ b/VAPs/quicklook/CLAP/CLAP_tutorial.ipynb @@ -0,0 +1,838 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# AOSCLAP3W.C1 Notebook\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/clap) for more information about this vap." + ] + }, + { + "cell_type": "markdown", + "id": "97097763", + "metadata": {}, + "source": [ + "In this notebook, we demonstrate the workflow to explore ARM vap data (using aosclap3w as an example.) Value-added products (VAPs) are higher-order data products that have been analyzed and processed to ease scientists’ use of ARM data in atmospheric research and global climate models. \n", + "Here is the main content we will cover." + ] + }, + { + "cell_type": "markdown", + "id": "eddec40f", + "metadata": {}, + "source": [ + "# Table of Content\n", + "## Access the data\n", + "* How to retrieve the data\n", + "* Data path and file name conventions\n", + "* Load data\n", + "## Explore the data\n", + "* NetCDF Data structure\n", + "* Xarray essentials\n", + "* Xarray Variable\n", + "## Plot the data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "import random\n", + "\n", + "import glob\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "2372d5be", + "metadata": {}, + "source": [ + "## Access the data" + ] + }, + { + "cell_type": "markdown", + "id": "5b0d9684", + "metadata": {}, + "source": [ + "### How to retrieve the data\n", + "We assume the path \"/data/archive\" is available where you are running this notebook. You can use `os.path.exists(\"/data/archive\")` to verify if the path exists at your machine.\n", + "\n", + "\n", + "### Data path and file name conventions\n", + "There several common terminologies regarding ARM data, for example, data-stream-name, data-level, site, facility, instrument, etc. (For more details, please see [ARM Data File Standards Version 1.2](https://www.google.com/search?q=arm+datastream+facility+cite+definition&rlz=1C1GCEJ_enUS1029US1029&ei=hb41ZICrBPukqtsPvMODIA&ved=0ahUKEwjAgeiS0aL-AhV7kmoFHbzhAAQQ4dUDCBA&uact=5&oq=arm+datastream+facility+cite+definition&gs_lcp=Cgxnd3Mtd2l6LXNlcnAQAzIFCAAQogQyBQgAEKIEMgUIABCiBDIFCAAQogQyBQgAEKIEOgoIABBHENYEELADOgoIIRCgARDDBBAKSgQIQRgAUJ4DWIoOYO0PaAFwAXgAgAGMAYgBuQmSAQMyLjmYAQCgAQHIAQjAAQE&sclient=gws-wiz-serp).) \n", + "\n", + "For example, this notebook is called `aosclap3w.c1`, where `aosclap3w` is the \"datastream name\", and the `{process.ds_class_level}}` is the \"data level\".\n", + "\n", + "This datastream also contains site `pvc` and facility `M1`. (Note: individual datastream might have multiple site-facility pairs.)\n", + "In such a case, the data of this data-stream is stored at `/data/archive/pvc/pvcaosclap3wM1.c1`, which is in the format of `//.`. We can use the following method to assign the data-stream directory `datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )`\n", + "\n", + "The data files under datastream_dir also follows naming conventions. But once reach the datastream_dir level, the most import file naming convention to differentiate the files is \"yyyyMMdd.hhmmss\", which comes handy to filter out files based on datetime. For example, we can use `glob.glob(f'{datastream_dir}/*.200709*.*')` to filter files in 2007 September.\n", + "\n", + "(Note: refer to the https://adc.arm.gov/discovery/#/ and https://adc.arm.gov/solr8/metadata/select API to explore ARM datastream and assoicated available sites and facilities\n", + "\n", + "Please see the following examples in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d7e9eb85", + "metadata": {}, + "outputs": [], + "source": [ + "# Verify if DATA_DIR path exists\n", + "DATA_DIR = \"/data/archive\"\n", + "os.path.exists(DATA_DIR)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "586993fd", + "metadata": {}, + "outputs": [], + "source": [ + "# Speicify datastream_dir following the path conventions and check its existence\n", + "DATASTREAM_NAME = \"aosclap3w\"\n", + "DATA_LEVEL = \"c1\"\n", + "site = \"pvc\"\n", + "facility = \"M1\"\n", + "datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )\n", + "print(datastream_dir)\n", + "print(os.path.exists(datastream_dir))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0742f7c1", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: list 5 (random) files under datastream_dir\n", + "files = os.listdir(datastream_dir)\n", + "files[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39b98a36", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: get most recent file\n", + "list_of_files = glob.glob(f\"{datastream_dir}/*\") # * means all if need specific format then *.csv\n", + "latest_file = max(list_of_files, key=os.path.getctime)\n", + "latest_file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "902d514e", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus sort datastream files based on datetime\n", + "files = os.listdir(datastream_dir)\n", + "file_sorted = files.copy()\n", + "file_sorted.sort() \n", + "print(file_sorted[:5])\n", + "\n", + "# to reverse\n", + "file_sorted_reverse = files.copy() \n", + "file_sorted_reverse.sort(reverse=True)\n", + "print(file_sorted_reverse[:5])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec5923b2", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: pattern matching\n", + "# filter the 200709** files under datastream_dir\n", + "files_filter = glob.glob(f'{datastream_dir}/*.200709*.*')\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c4a4aa18", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: use armnotebook_utils.file_filter (TODO) to filter files based on datastream info \n" + ] + }, + { + "cell_type": "markdown", + "id": "cfeb9efc", + "metadata": {}, + "source": [ + "### Load data\n", + "The arm data is stored in [NetCDF](https://en.wikipedia.org/wiki/NetCDF#:~:text=%22NetCDF%20(network%20Common%20Data%20Form,format%20for%20representing%20scientific%20data.) format. We can use xarray's `open_dataset` method to load single file, or `open_mfdataset` to open multiple files. (Note: the latter will still return a single xarray dataset object by combining multiple files.)\n", + "\n", + "Note: open_dataset keeps the file handle open and lazy loads its contents. All parameters are passed directly to open_dataset. It is a preferable method over load_dataset for memory-effiency. (more details, please see [xarray.open_dataset](https://docs.xarray.dev/en/stable/generated/xarray.open_dataset.html))\n", + "\n", + "See the following example in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a440a329", + "metadata": {}, + "outputs": [], + "source": [ + "# open single file\n", + "full_path = latest_file\n", + "print(full_path)\n", + "ds_single = xr.open_dataset(full_path)\n", + "ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0143a3d", + "metadata": {}, + "outputs": [], + "source": [ + "print(ds_single)\n", + "print(type(ds_single))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c0f8939", + "metadata": {}, + "outputs": [], + "source": [ + "# open multiple files\n", + "n_files = 3\n", + "full_paths = [os.path.join(datastream_dir, f_path) for f_path in file_sorted_reverse[:n_files]]\n", + "print(full_paths)\n", + "try: # Note: sometimes multiple files cannot be merged, so we used try except here.\n", + " ds_mutiple = xr.open_mfdataset(full_paths)\n", + " # ds_mutiple\n", + " print(type(ds_mutiple))\n", + " print(ds_mutiple)\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "3fa59943", + "metadata": {}, + "source": [ + "## Explore the data" + ] + }, + { + "cell_type": "markdown", + "id": "571f69b9", + "metadata": {}, + "source": [ + "### NetCDF Data structure \n", + "\n", + "\n", + "(If you are confident with NetCDF basics and xarray essentials, feel free to skip this session.)\n", + "Before we dive into data exploration, there are some eseential concpets we should be familiar with (shown below): \n", + "* Dataset\n", + "* Data array\n", + "* Variable\n", + "* Dimenssion\n", + "* Coordinate\n", + "* Data Type\n", + "* Meta Data (Attributes)\n", + "\n", + "We will not go into details about NetCDF basics and here are some references you might find helpful\n", + "* [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#)\n", + "* [Network Common Data Form (NetCDF)](https://www.unidata.ucar.edu/software/netcdf/) and [A Brief History of (netCDF) Time](https://www.unidata.ucar.edu/software/netcdf/time/recs.html)\n", + "\n", + "\n", + "\n", + "### Xarray essentials\n", + "Earlier we introduced the xarray pacakge and used `open_dataset` and `open_mfdataset` to retrieve NetCDF data file as an xarray Dataset object. Recall that you can review the data in a notebook by using `print(ds)` or `ds`. \n", + "\n", + "Using xarray to retrieve the aforementioned NetCDF basics is straightforwared. In general,\n", + "* Dataset: `ds`\n", + "* Data array: `ds.variables`\n", + "* Variable: `ds.ds.variables`\n", + "* Dimenssion: `ds.dims`\n", + "* Coordinate: `ds.coords`\n", + "* Data Type: `type`\n", + "* Meta Data (Attributes)\n", + "\n", + "Also, here are some references if you are new to xarray\n", + "* [Xarray in 45 minutes](https://tutorial.xarray.dev/overview/xarray-in-45-min.html)\n", + "* [Handling NetCDF Files using XArray for Absolute Beginners](https://towardsdatascience.com/handling-netcdf-files-using-xarray-for-absolute-beginners-111a8ab4463f)\n", + "\n", + "Try the following commands in action\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b3eac323", + "metadata": {}, + "outputs": [], + "source": [ + "ds = ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77ecf85d", + "metadata": {}, + "outputs": [], + "source": [ + "# Dataset \n", + "ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25e7de09", + "metadata": {}, + "outputs": [], + "source": [ + "# Data array, Variable\n", + "# Note: the info can be overwhelming. Do not attempt to grasp everything at the first glance.\n", + "ds.variables" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c41b67e", + "metadata": {}, + "outputs": [], + "source": [ + "# dimenssions\n", + "ds.dims" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "156f1dfc", + "metadata": {}, + "outputs": [], + "source": [ + "# coordinates\n", + "ds.coords" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "277d6064", + "metadata": {}, + "outputs": [], + "source": [ + "# Meta Data (Attributes)\n", + "ds.attrs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d334681f", + "metadata": {}, + "outputs": [], + "source": [ + "# type\n", + "print(type(ds))\n", + "print(type(ds.variables))\n", + "print(type(ds.dims))\n", + "print(type(ds.coords))\n", + "print(type(ds.attrs))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "643399d6", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: retrieve variable names only\n", + "list(ds.variables)" + ] + }, + { + "cell_type": "markdown", + "id": "1cf36878", + "metadata": {}, + "source": [ + "#### Discussion: variable vs. coordinates vs. dimenssions. \n", + "From [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#):\n", + "\n", + "* Dimession: A dimension may be used to represent a real physical dimension, for example, time, latitude, longitude, or height. A dimension might also be used to index other quantities, for example station or model-run-number.\n", + "A netCDF dimension has both a name and a length. A dimension length is an arbitrary positive integer, except that one dimension in a netCDF dataset can have the length UNLIMITED.\n", + "\n", + "* Variables: Variables are used to store the bulk of the data in a netCDF dataset. A variable represents an array of values of the same type. A scalar value is treated as a 0-dimensional array. A variable has a name, a data type, and a shape described by its list of dimensions specified when the variable is created. A variable may also have associated attributes, which may be added, deleted or changed after the variable is created.\n", + "\n", + "* Coordinate (Variables): It is legal for a variable to have the same name as a dimension. Such variables have no special meaning to the netCDF library. However there is a convention that such variables should be treated in a special way by software using this library.\n", + "A variable with the same name as a dimension is called a coordinate variable. It typically defines a physical coordinate corresponding to that dimension.\n", + "\n", + "\n", + "Tips: By definition, coordinates and dimenssions are also variable. For example ds.time is a coordinate but it is also a special variable. But in practice, when we talk about variable, it implies regular/non-coordinate variable. We will use this convention for the remaining notebook. " + ] + }, + { + "cell_type": "markdown", + "id": "d47120f9", + "metadata": {}, + "source": [ + "### Xarray Variable\n", + "\n", + "\n", + "We can use `ds.variables` to access varialbes of a dataset. But many times viewing print-out the whole datasets can be overwhemling. Instead, we would work on the individual variable (also called Xarray data array.) using the `ds[var_name]` syntax, where `var_name` is the name of the variable. See the following examples in action.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5a57e136", + "metadata": {}, + "outputs": [], + "source": [ + "print(type(ds[\"time\"]))\n", + "ds[\"time\"]" + ] + }, + { + "cell_type": "markdown", + "id": "51ec643e", + "metadata": {}, + "source": [ + "#### Variable properties\n", + "For individual varible, the following properties are mostly used with the assocaited xarray method to retrieve them. (assuming var = ds[var_name])\n", + "* name: `var.name`\n", + "* data content: `var.data`\n", + "* attributes: `var.attrs`\n", + "* dimenstions: `var.dims`\n", + "* data type: `var.data.dtype`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a632a525", + "metadata": {}, + "outputs": [], + "source": [ + "var_name = \"time\"\n", + "var = ds[var_name]\n", + "\n", + "print(\"var.name: \\n\", var.name, \"\\n\")\n", + "print(\"var.data: \\n\", var.data, \"\\n\")\n", + "print(\"var.attrs: \\n\", var.attrs, \"\\n\")\n", + "print(\"var.dims: \\n\", var.dims, \"\\n\")\n", + "print(\"var.data.dtype: \\n\", var.data.dtype, \"\\n\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b256b07f", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: indivdual variable info table (collect and display variable info as a tabular format)\n", + "df_info = pd.DataFrame()\n", + "df_info[\"var_name\"] = list(ds.variables)\n", + "df_info[\"dims\"] = df_info.var_name.apply(lambda x: ds[x].dims)\n", + "df_info[\"is_dim\"] = df_info.var_name.apply(lambda x: x in ds.dims)\n", + "df_info[\"n_dim\"] = df_info.var_name.apply(lambda x: len(ds[x].dims))\n", + "df_info[\"attrs\"] = df_info.var_name.apply(lambda x: ds[x].attrs)\n", + "df_info[\"dtype\"] = df_info.var_name.apply(lambda x: ds[x].data.dtype)\n", + "df_info" + ] + }, + { + "cell_type": "markdown", + "id": "e6a36b96", + "metadata": {}, + "source": [ + "### Data cleaning/Preprocessing (skipped)\n", + "Data cleaning and preprocessing is an important stage in the the data analysis pipeline. However it will be out of the scope of this notebook. For more information about data cleaning and preprocessing basics, here are some references. \n", + "* [Xarray Fundamentals](https://earth-env-data-science.github.io/lectures/xarray/xarray.html)\n", + "* [Xarray Tutorial — Pangeo Gallery documentation](http://gallery.pangeo.io/repos/pangeo-data/pangeo-tutorial-gallery/xarray.html)\n", + "* [Pythonic Data Cleaning With pandas and NumPy](https://realpython.com/python-data-cleaning-numpy-pandas/)\n", + "* [Pandas - Cleaning Data](https://www.w3schools.com/python/pandas/pandas_cleaning.asp)" + ] + }, + { + "cell_type": "markdown", + "id": "ae6e1a69", + "metadata": {}, + "source": [ + "### Plotting\n", + "Data visualization (or plotting) is another important data analysis topic and deserves its own discussion. Here in this notebook we will only demonstrate basic tool for simple data visualization tasks.\n", + "\n", + "Here are some reference you might find useful:\n", + "* xarray plotting: https://docs.xarray.dev/en/stable/user-guide/plotting.html\n", + "* Atmospheric Community Toolkit (ACT): https://arm-doe.github.io/ACT/index.html\n", + "\n", + "Note: this notebook is auto-generated using a template. It uses a general idea to select variable(s) to plot and is not customized to each indivdual notebook. Feel free to change the variables in intrrests, especially certain figure is failed to plot." + ] + }, + { + "cell_type": "markdown", + "id": "7e417fb2", + "metadata": {}, + "source": [ + "#### 1-dimenssional basic time series plot\n", + "\n", + "For the following plot we would like to find variables such that\n", + "* it has one and only one dimession\n", + "* \"time\" is its coordinate variable\n", + "* it is not a dimenssion itself,\n", + "* it is not a special variable with substrings within [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "\n", + "Please see the following example in action. (For more details about pandas filtering, please see the following references.)\n", + "* [pandas: multiple conditions while indexing data frame](https://stackoverflow.com/questions/22591174/pandas-multiple-conditions-while-indexing-data-frame-unexpected-behavior)\n", + "* [pandas.DataFrame.apply](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.apply.html)\n", + "* [How to test if a string contains one of the substrings in a list...](https://stackoverflow.com/questions/26577516/how-to-test-if-a-string-contains-one-of-the-substrings-in-a-list-in-pandas" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30edac2d", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==1, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter = df_info[(df_info.n_dim == 1) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4777a659", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_1d = df_filter.var_name.values[0]\n", + " var_1d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb5ae985", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " ds[var_1d].plot()\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "9e41066e", + "metadata": {}, + "source": [ + "#### 2-dimenssional basic plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09bd4adc", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==2, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_2 = df_info[(df_info.n_dim == 2) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2cdea1a9", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_2d = df_filter_2.var_name.values[0]\n", + " var_2d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5f48879", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " print(ds[var_2d].dims)\n", + " # ds[var_2d].plot()\n", + "\n", + " # conventionally, use \"time\" as x-axis\n", + " ds[var_2d].plot(x=\"time\")\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "7554b2a6", + "metadata": {}, + "source": [ + "#### qc-plotting (optional)\n", + "\n", + "Note: act qc plotting has more strict requirement, one of them is the associated qc variable need to have \"flag_masks\" attributes. Which is added by using ds = act.io.armfiles.read_netcdf(files_list), then ds.clean.cleanup()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71c2096f", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " ds_act = act.io.armfiles.read_netcdf(full_path)\n", + " print(type(ds_act))\n", + " ds_act.clean.cleanup()\n", + "\n", + " # or \n", + " # ds.clean.cleanup()\n", + "except Exception as e:\n", + " print(\"ERROR\", e)\n", + " ds_act = ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "844f8505", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter valid variables for ACT qc plotting\n", + "condition = (\"qc_\" + df_info.var_name).apply(lambda x: ds_act[x].attrs.get(\"flag_masks\") is not None \n", + " if x in list(ds.variables) else False)\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_3 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings))) &\n", + " condition\n", + " ]\n", + "df_filter_3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd38cc6", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "try:\n", + " qc_variable = df_filter_3.var_name.values[0]\n", + " print(qc_variable)\n", + "except Exception as e:\n", + " print(e)\n", + " \n", + "try:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + " qc_ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "d76e4d27", + "metadata": {}, + "source": [ + "#### bonus: choose variables to plot from a dropdown menu " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb733804", + "metadata": {}, + "outputs": [], + "source": [ + "# Valid variables filtering\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_4 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82d5c20d", + "metadata": {}, + "outputs": [], + "source": [ + "# example 1: using xarray plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2fc9aaa4", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# fig, ax = plt.subplots(figsize=(10, 4))\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "# fig.clear() # Remove old lines from plot and plot new one\n", + "# if len(ds[var].dims)==2:\n", + "# ds[var].plot(x=\"time\", add_colorbar=False)\n", + "# else:\n", + "# ds[var].plot()\n", + "# plt.grid()\n", + "# plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fabdd802", + "metadata": {}, + "outputs": [], + "source": [ + "# example 2: using act plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "962b4186", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "\n", + "\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "\n", + "# i_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + "# i_display.add_subplots((1,), figsize=(10, 4))\n", + "# ax = i_display.plot(var, subplot_index=(0,), set_title=f\"{var} ({ds_act[var].attrs['long_name']})\",)\n", + "\n", + "# ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "# ax.grid()\n", + "# plt.show()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/CLAP/aosclap3w.c1.ipynb b/VAPs/quicklook/CLAP/aosclap3w.c1.ipynb new file mode 100644 index 00000000..728c580e --- /dev/null +++ b/VAPs/quicklook/CLAP/aosclap3w.c1.ipynb @@ -0,0 +1,339 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# AOSCLAP3W.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/clap) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'aosclap3w'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2013-06-24', 'facility': 'M1', 'site': 'pvc', 'start_date': '2012-07-16'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'pvc', 'M1' )\n", + "\n", + "date_start = '2013-06-22'\n", + "date_end = '2013-06-24'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['Ba_B_CLAP3W_1', 'Ba_G_CLAP3W_1', 'Ba_R_CLAP3W_1']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'Ba_B_CLAP3W_1'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'Ba_B_CLAP3W_1'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/CLDTYPE/CLDTYPE_tutorial.ipynb b/VAPs/quicklook/CLDTYPE/CLDTYPE_tutorial.ipynb new file mode 100644 index 00000000..41ff471b --- /dev/null +++ b/VAPs/quicklook/CLDTYPE/CLDTYPE_tutorial.ipynb @@ -0,0 +1,838 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# CLDTYPE.C1 Notebook\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/cldtype) for more information about this vap." + ] + }, + { + "cell_type": "markdown", + "id": "97097763", + "metadata": {}, + "source": [ + "In this notebook, we demonstrate the workflow to explore ARM vap data (using cldtype as an example.) Value-added products (VAPs) are higher-order data products that have been analyzed and processed to ease scientists’ use of ARM data in atmospheric research and global climate models. \n", + "Here is the main content we will cover." + ] + }, + { + "cell_type": "markdown", + "id": "eddec40f", + "metadata": {}, + "source": [ + "# Table of Content\n", + "## Access the data\n", + "* How to retrieve the data\n", + "* Data path and file name conventions\n", + "* Load data\n", + "## Explore the data\n", + "* NetCDF Data structure\n", + "* Xarray essentials\n", + "* Xarray Variable\n", + "## Plot the data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "import random\n", + "\n", + "import glob\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "2372d5be", + "metadata": {}, + "source": [ + "## Access the data" + ] + }, + { + "cell_type": "markdown", + "id": "5b0d9684", + "metadata": {}, + "source": [ + "### How to retrieve the data\n", + "We assume the path \"/data/archive\" is available where you are running this notebook. You can use `os.path.exists(\"/data/archive\")` to verify if the path exists at your machine.\n", + "\n", + "\n", + "### Data path and file name conventions\n", + "There several common terminologies regarding ARM data, for example, data-stream-name, data-level, site, facility, instrument, etc. (For more details, please see [ARM Data File Standards Version 1.2](https://www.google.com/search?q=arm+datastream+facility+cite+definition&rlz=1C1GCEJ_enUS1029US1029&ei=hb41ZICrBPukqtsPvMODIA&ved=0ahUKEwjAgeiS0aL-AhV7kmoFHbzhAAQQ4dUDCBA&uact=5&oq=arm+datastream+facility+cite+definition&gs_lcp=Cgxnd3Mtd2l6LXNlcnAQAzIFCAAQogQyBQgAEKIEMgUIABCiBDIFCAAQogQyBQgAEKIEOgoIABBHENYEELADOgoIIRCgARDDBBAKSgQIQRgAUJ4DWIoOYO0PaAFwAXgAgAGMAYgBuQmSAQMyLjmYAQCgAQHIAQjAAQE&sclient=gws-wiz-serp).) \n", + "\n", + "For example, this notebook is called `cldtype.c1`, where `cldtype` is the \"datastream name\", and the `{process.ds_class_level}}` is the \"data level\".\n", + "\n", + "This datastream also contains site `anx` and facility `M1`. (Note: individual datastream might have multiple site-facility pairs.)\n", + "In such a case, the data of this data-stream is stored at `/data/archive/anx/anxcldtypeM1.c1`, which is in the format of `//.`. We can use the following method to assign the data-stream directory `datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )`\n", + "\n", + "The data files under datastream_dir also follows naming conventions. But once reach the datastream_dir level, the most import file naming convention to differentiate the files is \"yyyyMMdd.hhmmss\", which comes handy to filter out files based on datetime. For example, we can use `glob.glob(f'{datastream_dir}/*.200709*.*')` to filter files in 2007 September.\n", + "\n", + "(Note: refer to the https://adc.arm.gov/discovery/#/ and https://adc.arm.gov/solr8/metadata/select API to explore ARM datastream and assoicated available sites and facilities\n", + "\n", + "Please see the following examples in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d7e9eb85", + "metadata": {}, + "outputs": [], + "source": [ + "# Verify if DATA_DIR path exists\n", + "DATA_DIR = \"/data/archive\"\n", + "os.path.exists(DATA_DIR)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "586993fd", + "metadata": {}, + "outputs": [], + "source": [ + "# Speicify datastream_dir following the path conventions and check its existence\n", + "DATASTREAM_NAME = \"cldtype\"\n", + "DATA_LEVEL = \"c1\"\n", + "site = \"anx\"\n", + "facility = \"M1\"\n", + "datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )\n", + "print(datastream_dir)\n", + "print(os.path.exists(datastream_dir))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0742f7c1", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: list 5 (random) files under datastream_dir\n", + "files = os.listdir(datastream_dir)\n", + "files[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39b98a36", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: get most recent file\n", + "list_of_files = glob.glob(f\"{datastream_dir}/*\") # * means all if need specific format then *.csv\n", + "latest_file = max(list_of_files, key=os.path.getctime)\n", + "latest_file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "902d514e", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus sort datastream files based on datetime\n", + "files = os.listdir(datastream_dir)\n", + "file_sorted = files.copy()\n", + "file_sorted.sort() \n", + "print(file_sorted[:5])\n", + "\n", + "# to reverse\n", + "file_sorted_reverse = files.copy() \n", + "file_sorted_reverse.sort(reverse=True)\n", + "print(file_sorted_reverse[:5])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec5923b2", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: pattern matching\n", + "# filter the 200709** files under datastream_dir\n", + "files_filter = glob.glob(f'{datastream_dir}/*.200709*.*')\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c4a4aa18", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: use armnotebook_utils.file_filter (TODO) to filter files based on datastream info \n" + ] + }, + { + "cell_type": "markdown", + "id": "cfeb9efc", + "metadata": {}, + "source": [ + "### Load data\n", + "The arm data is stored in [NetCDF](https://en.wikipedia.org/wiki/NetCDF#:~:text=%22NetCDF%20(network%20Common%20Data%20Form,format%20for%20representing%20scientific%20data.) format. We can use xarray's `open_dataset` method to load single file, or `open_mfdataset` to open multiple files. (Note: the latter will still return a single xarray dataset object by combining multiple files.)\n", + "\n", + "Note: open_dataset keeps the file handle open and lazy loads its contents. All parameters are passed directly to open_dataset. It is a preferable method over load_dataset for memory-effiency. (more details, please see [xarray.open_dataset](https://docs.xarray.dev/en/stable/generated/xarray.open_dataset.html))\n", + "\n", + "See the following example in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a440a329", + "metadata": {}, + "outputs": [], + "source": [ + "# open single file\n", + "full_path = latest_file\n", + "print(full_path)\n", + "ds_single = xr.open_dataset(full_path)\n", + "ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0143a3d", + "metadata": {}, + "outputs": [], + "source": [ + "print(ds_single)\n", + "print(type(ds_single))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c0f8939", + "metadata": {}, + "outputs": [], + "source": [ + "# open multiple files\n", + "n_files = 3\n", + "full_paths = [os.path.join(datastream_dir, f_path) for f_path in file_sorted_reverse[:n_files]]\n", + "print(full_paths)\n", + "try: # Note: sometimes multiple files cannot be merged, so we used try except here.\n", + " ds_mutiple = xr.open_mfdataset(full_paths)\n", + " # ds_mutiple\n", + " print(type(ds_mutiple))\n", + " print(ds_mutiple)\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "3fa59943", + "metadata": {}, + "source": [ + "## Explore the data" + ] + }, + { + "cell_type": "markdown", + "id": "571f69b9", + "metadata": {}, + "source": [ + "### NetCDF Data structure \n", + "\n", + "\n", + "(If you are confident with NetCDF basics and xarray essentials, feel free to skip this session.)\n", + "Before we dive into data exploration, there are some eseential concpets we should be familiar with (shown below): \n", + "* Dataset\n", + "* Data array\n", + "* Variable\n", + "* Dimenssion\n", + "* Coordinate\n", + "* Data Type\n", + "* Meta Data (Attributes)\n", + "\n", + "We will not go into details about NetCDF basics and here are some references you might find helpful\n", + "* [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#)\n", + "* [Network Common Data Form (NetCDF)](https://www.unidata.ucar.edu/software/netcdf/) and [A Brief History of (netCDF) Time](https://www.unidata.ucar.edu/software/netcdf/time/recs.html)\n", + "\n", + "\n", + "\n", + "### Xarray essentials\n", + "Earlier we introduced the xarray pacakge and used `open_dataset` and `open_mfdataset` to retrieve NetCDF data file as an xarray Dataset object. Recall that you can review the data in a notebook by using `print(ds)` or `ds`. \n", + "\n", + "Using xarray to retrieve the aforementioned NetCDF basics is straightforwared. In general,\n", + "* Dataset: `ds`\n", + "* Data array: `ds.variables`\n", + "* Variable: `ds.ds.variables`\n", + "* Dimenssion: `ds.dims`\n", + "* Coordinate: `ds.coords`\n", + "* Data Type: `type`\n", + "* Meta Data (Attributes)\n", + "\n", + "Also, here are some references if you are new to xarray\n", + "* [Xarray in 45 minutes](https://tutorial.xarray.dev/overview/xarray-in-45-min.html)\n", + "* [Handling NetCDF Files using XArray for Absolute Beginners](https://towardsdatascience.com/handling-netcdf-files-using-xarray-for-absolute-beginners-111a8ab4463f)\n", + "\n", + "Try the following commands in action\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b3eac323", + "metadata": {}, + "outputs": [], + "source": [ + "ds = ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77ecf85d", + "metadata": {}, + "outputs": [], + "source": [ + "# Dataset \n", + "ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25e7de09", + "metadata": {}, + "outputs": [], + "source": [ + "# Data array, Variable\n", + "# Note: the info can be overwhelming. Do not attempt to grasp everything at the first glance.\n", + "ds.variables" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c41b67e", + "metadata": {}, + "outputs": [], + "source": [ + "# dimenssions\n", + "ds.dims" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "156f1dfc", + "metadata": {}, + "outputs": [], + "source": [ + "# coordinates\n", + "ds.coords" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "277d6064", + "metadata": {}, + "outputs": [], + "source": [ + "# Meta Data (Attributes)\n", + "ds.attrs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d334681f", + "metadata": {}, + "outputs": [], + "source": [ + "# type\n", + "print(type(ds))\n", + "print(type(ds.variables))\n", + "print(type(ds.dims))\n", + "print(type(ds.coords))\n", + "print(type(ds.attrs))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "643399d6", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: retrieve variable names only\n", + "list(ds.variables)" + ] + }, + { + "cell_type": "markdown", + "id": "1cf36878", + "metadata": {}, + "source": [ + "#### Discussion: variable vs. coordinates vs. dimenssions. \n", + "From [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#):\n", + "\n", + "* Dimession: A dimension may be used to represent a real physical dimension, for example, time, latitude, longitude, or height. A dimension might also be used to index other quantities, for example station or model-run-number.\n", + "A netCDF dimension has both a name and a length. A dimension length is an arbitrary positive integer, except that one dimension in a netCDF dataset can have the length UNLIMITED.\n", + "\n", + "* Variables: Variables are used to store the bulk of the data in a netCDF dataset. A variable represents an array of values of the same type. A scalar value is treated as a 0-dimensional array. A variable has a name, a data type, and a shape described by its list of dimensions specified when the variable is created. A variable may also have associated attributes, which may be added, deleted or changed after the variable is created.\n", + "\n", + "* Coordinate (Variables): It is legal for a variable to have the same name as a dimension. Such variables have no special meaning to the netCDF library. However there is a convention that such variables should be treated in a special way by software using this library.\n", + "A variable with the same name as a dimension is called a coordinate variable. It typically defines a physical coordinate corresponding to that dimension.\n", + "\n", + "\n", + "Tips: By definition, coordinates and dimenssions are also variable. For example ds.time is a coordinate but it is also a special variable. But in practice, when we talk about variable, it implies regular/non-coordinate variable. We will use this convention for the remaining notebook. " + ] + }, + { + "cell_type": "markdown", + "id": "d47120f9", + "metadata": {}, + "source": [ + "### Xarray Variable\n", + "\n", + "\n", + "We can use `ds.variables` to access varialbes of a dataset. But many times viewing print-out the whole datasets can be overwhemling. Instead, we would work on the individual variable (also called Xarray data array.) using the `ds[var_name]` syntax, where `var_name` is the name of the variable. See the following examples in action.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5a57e136", + "metadata": {}, + "outputs": [], + "source": [ + "print(type(ds[\"time\"]))\n", + "ds[\"time\"]" + ] + }, + { + "cell_type": "markdown", + "id": "51ec643e", + "metadata": {}, + "source": [ + "#### Variable properties\n", + "For individual varible, the following properties are mostly used with the assocaited xarray method to retrieve them. (assuming var = ds[var_name])\n", + "* name: `var.name`\n", + "* data content: `var.data`\n", + "* attributes: `var.attrs`\n", + "* dimenstions: `var.dims`\n", + "* data type: `var.data.dtype`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a632a525", + "metadata": {}, + "outputs": [], + "source": [ + "var_name = \"time\"\n", + "var = ds[var_name]\n", + "\n", + "print(\"var.name: \\n\", var.name, \"\\n\")\n", + "print(\"var.data: \\n\", var.data, \"\\n\")\n", + "print(\"var.attrs: \\n\", var.attrs, \"\\n\")\n", + "print(\"var.dims: \\n\", var.dims, \"\\n\")\n", + "print(\"var.data.dtype: \\n\", var.data.dtype, \"\\n\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b256b07f", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: indivdual variable info table (collect and display variable info as a tabular format)\n", + "df_info = pd.DataFrame()\n", + "df_info[\"var_name\"] = list(ds.variables)\n", + "df_info[\"dims\"] = df_info.var_name.apply(lambda x: ds[x].dims)\n", + "df_info[\"is_dim\"] = df_info.var_name.apply(lambda x: x in ds.dims)\n", + "df_info[\"n_dim\"] = df_info.var_name.apply(lambda x: len(ds[x].dims))\n", + "df_info[\"attrs\"] = df_info.var_name.apply(lambda x: ds[x].attrs)\n", + "df_info[\"dtype\"] = df_info.var_name.apply(lambda x: ds[x].data.dtype)\n", + "df_info" + ] + }, + { + "cell_type": "markdown", + "id": "e6a36b96", + "metadata": {}, + "source": [ + "### Data cleaning/Preprocessing (skipped)\n", + "Data cleaning and preprocessing is an important stage in the the data analysis pipeline. However it will be out of the scope of this notebook. For more information about data cleaning and preprocessing basics, here are some references. \n", + "* [Xarray Fundamentals](https://earth-env-data-science.github.io/lectures/xarray/xarray.html)\n", + "* [Xarray Tutorial — Pangeo Gallery documentation](http://gallery.pangeo.io/repos/pangeo-data/pangeo-tutorial-gallery/xarray.html)\n", + "* [Pythonic Data Cleaning With pandas and NumPy](https://realpython.com/python-data-cleaning-numpy-pandas/)\n", + "* [Pandas - Cleaning Data](https://www.w3schools.com/python/pandas/pandas_cleaning.asp)" + ] + }, + { + "cell_type": "markdown", + "id": "ae6e1a69", + "metadata": {}, + "source": [ + "### Plotting\n", + "Data visualization (or plotting) is another important data analysis topic and deserves its own discussion. Here in this notebook we will only demonstrate basic tool for simple data visualization tasks.\n", + "\n", + "Here are some reference you might find useful:\n", + "* xarray plotting: https://docs.xarray.dev/en/stable/user-guide/plotting.html\n", + "* Atmospheric Community Toolkit (ACT): https://arm-doe.github.io/ACT/index.html\n", + "\n", + "Note: this notebook is auto-generated using a template. It uses a general idea to select variable(s) to plot and is not customized to each indivdual notebook. Feel free to change the variables in intrrests, especially certain figure is failed to plot." + ] + }, + { + "cell_type": "markdown", + "id": "7e417fb2", + "metadata": {}, + "source": [ + "#### 1-dimenssional basic time series plot\n", + "\n", + "For the following plot we would like to find variables such that\n", + "* it has one and only one dimession\n", + "* \"time\" is its coordinate variable\n", + "* it is not a dimenssion itself,\n", + "* it is not a special variable with substrings within [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "\n", + "Please see the following example in action. (For more details about pandas filtering, please see the following references.)\n", + "* [pandas: multiple conditions while indexing data frame](https://stackoverflow.com/questions/22591174/pandas-multiple-conditions-while-indexing-data-frame-unexpected-behavior)\n", + "* [pandas.DataFrame.apply](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.apply.html)\n", + "* [How to test if a string contains one of the substrings in a list...](https://stackoverflow.com/questions/26577516/how-to-test-if-a-string-contains-one-of-the-substrings-in-a-list-in-pandas" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30edac2d", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==1, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter = df_info[(df_info.n_dim == 1) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4777a659", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_1d = df_filter.var_name.values[0]\n", + " var_1d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb5ae985", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " ds[var_1d].plot()\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "9e41066e", + "metadata": {}, + "source": [ + "#### 2-dimenssional basic plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09bd4adc", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==2, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_2 = df_info[(df_info.n_dim == 2) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2cdea1a9", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_2d = df_filter_2.var_name.values[0]\n", + " var_2d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5f48879", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " print(ds[var_2d].dims)\n", + " # ds[var_2d].plot()\n", + "\n", + " # conventionally, use \"time\" as x-axis\n", + " ds[var_2d].plot(x=\"time\")\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "7554b2a6", + "metadata": {}, + "source": [ + "#### qc-plotting (optional)\n", + "\n", + "Note: act qc plotting has more strict requirement, one of them is the associated qc variable need to have \"flag_masks\" attributes. Which is added by using ds = act.io.armfiles.read_netcdf(files_list), then ds.clean.cleanup()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71c2096f", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " ds_act = act.io.armfiles.read_netcdf(full_path)\n", + " print(type(ds_act))\n", + " ds_act.clean.cleanup()\n", + "\n", + " # or \n", + " # ds.clean.cleanup()\n", + "except Exception as e:\n", + " print(\"ERROR\", e)\n", + " ds_act = ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "844f8505", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter valid variables for ACT qc plotting\n", + "condition = (\"qc_\" + df_info.var_name).apply(lambda x: ds_act[x].attrs.get(\"flag_masks\") is not None \n", + " if x in list(ds.variables) else False)\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_3 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings))) &\n", + " condition\n", + " ]\n", + "df_filter_3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd38cc6", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "try:\n", + " qc_variable = df_filter_3.var_name.values[0]\n", + " print(qc_variable)\n", + "except Exception as e:\n", + " print(e)\n", + " \n", + "try:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + " qc_ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "d76e4d27", + "metadata": {}, + "source": [ + "#### bonus: choose variables to plot from a dropdown menu " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb733804", + "metadata": {}, + "outputs": [], + "source": [ + "# Valid variables filtering\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_4 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82d5c20d", + "metadata": {}, + "outputs": [], + "source": [ + "# example 1: using xarray plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2fc9aaa4", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# fig, ax = plt.subplots(figsize=(10, 4))\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "# fig.clear() # Remove old lines from plot and plot new one\n", + "# if len(ds[var].dims)==2:\n", + "# ds[var].plot(x=\"time\", add_colorbar=False)\n", + "# else:\n", + "# ds[var].plot()\n", + "# plt.grid()\n", + "# plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fabdd802", + "metadata": {}, + "outputs": [], + "source": [ + "# example 2: using act plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "962b4186", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "\n", + "\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "\n", + "# i_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + "# i_display.add_subplots((1,), figsize=(10, 4))\n", + "# ax = i_display.plot(var, subplot_index=(0,), set_title=f\"{var} ({ds_act[var].attrs['long_name']})\",)\n", + "\n", + "# ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "# ax.grid()\n", + "# plt.show()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/CLDTYPE/cldtype.c1.ipynb b/VAPs/quicklook/CLDTYPE/cldtype.c1.ipynb new file mode 100644 index 00000000..c7e2636a --- /dev/null +++ b/VAPs/quicklook/CLDTYPE/cldtype.c1.ipynb @@ -0,0 +1,339 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# CLDTYPE.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/cldtype) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'cldtype'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2020-05-31', 'facility': 'M1', 'site': 'anx', 'start_date': '2019-12-01'}, {'end_date': '2023-08-30', 'facility': 'C1', 'site': 'ena', 'start_date': '2015-07-17'}, {'end_date': '2022-09-30', 'facility': 'M1', 'site': 'hou', 'start_date': '2021-10-01'}, {'end_date': '2023-06-15', 'facility': 'M1', 'site': 'guc', 'start_date': '2021-09-14'}, {'end_date': '2019-04-30', 'facility': 'M1', 'site': 'cor', 'start_date': '2018-09-23'}, {'end_date': '2023-08-30', 'facility': 'C1', 'site': 'nsa', 'start_date': '1998-03-25'}, {'end_date': '2023-08-30', 'facility': 'C1', 'site': 'sgp', 'start_date': '1996-11-08'}, {'end_date': '2014-05-03', 'facility': 'C1', 'site': 'twp', 'start_date': '1999-07-01'}, {'end_date': '2009-02-14', 'facility': 'C2', 'site': 'twp', 'start_date': '1998-11-01'}, {'end_date': '2014-05-03', 'facility': 'C3', 'site': 'twp', 'start_date': '2003-01-01'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2023-08-28'\n", + "date_end = '2023-08-30'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['cloudtype', 'cloud_base_best_estimate', 'cloud_layer_top_height']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'cloudtype'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'cloudtype'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/CMAC2/.ipynb_checkpoints/cmac2.c1-checkpoint.ipynb b/VAPs/quicklook/CMAC2/.ipynb_checkpoints/cmac2.c1-checkpoint.ipynb new file mode 100644 index 00000000..425a9cbc --- /dev/null +++ b/VAPs/quicklook/CMAC2/.ipynb_checkpoints/cmac2.c1-checkpoint.ipynb @@ -0,0 +1,3537 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# CMAC2.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/cmac2) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'cmac2'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2019-01-22', 'facility': 'I4', 'site': 'sgp', 'start_date': '2018-08-30'}, {'end_date': '2019-04-05', 'facility': 'I5', 'site': 'sgp', 'start_date': '2018-08-30'}, {'end_date': '2019-02-26', 'facility': 'I6', 'site': 'sgp', 'start_date': '2018-08-30'}]" + ] + }, + { + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "ac6764f5", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The following locations and date ranges are available for this VAP:\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
sitefacilitystart_dateend_date
0sgpI42018-08-302019-01-22
1sgpI52018-08-302019-04-05
2sgpI62018-08-302019-02-26
\n", + "
" + ], + "text/plain": [ + " site facility start_date end_date\n", + "0 sgp I4 2018-08-30 2019-01-22\n", + "1 sgp I5 2018-08-30 2019-04-05\n", + "2 sgp I6 2018-08-30 2019-02-26" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'I4' )\n", + "\n", + "date_start = '2019-01-21'\n", + "date_end = '2019-01-22'" + ] + }, + { + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'/data/archive/sgp/sgpcmac2I4.c1'" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['20190121', '20190122']" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "51feea2e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['/data/archive/sgp/sgpcmac2I4.c1/sgpcmac2I4.c1.20190122.020009.nc',\n", + " '/data/archive/sgp/sgpcmac2I4.c1/sgpcmac2I4.c1.20190122.044928.nc',\n", + " '/data/archive/sgp/sgpcmac2I4.c1/sgpcmac2I4.c1.20190122.024236.nc',\n", + " '/data/archive/sgp/sgpcmac2I4.c1/sgpcmac2I4.c1.20190122.005559.nc',\n", + " '/data/archive/sgp/sgpcmac2I4.c1/sgpcmac2I4.c1.20190122.003457.nc',\n", + " '/data/archive/sgp/sgpcmac2I4.c1/sgpcmac2I4.c1.20190122.034552.nc',\n", + " '/data/archive/sgp/sgpcmac2I4.c1/sgpcmac2I4.c1.20190122.042813.nc',\n", + " '/data/archive/sgp/sgpcmac2I4.c1/sgpcmac2I4.c1.20190122.022111.nc',\n", + " '/data/archive/sgp/sgpcmac2I4.c1/sgpcmac2I4.c1.20190122.030339.nc',\n", + " '/data/archive/sgp/sgpcmac2I4.c1/sgpcmac2I4.c1.20190122.051031.nc',\n", + " '/data/archive/sgp/sgpcmac2I4.c1/sgpcmac2I4.c1.20190122.040656.nc',\n", + " '/data/archive/sgp/sgpcmac2I4.c1/sgpcmac2I4.c1.20190122.011706.nc',\n", + " '/data/archive/sgp/sgpcmac2I4.c1/sgpcmac2I4.c1.20190122.013821.nc',\n", + " '/data/archive/sgp/sgpcmac2I4.c1/sgpcmac2I4.c1.20190122.032444.nc',\n", + " '/data/archive/sgp/sgpcmac2I4.c1/sgpcmac2I4.c1.20190122.001346.nc']" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "15 files loaded\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/kefeimo/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/act/qc/clean.py:234: RuntimeWarning: invalid value encountered in cast\n", + " data = data.astype(dtype)\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
<xarray.Dataset>\n",
+       "Dimensions:                                   (time: 97200, range: 1001,\n",
+       "                                               sweep: 18)\n",
+       "Coordinates:\n",
+       "  * time                                      (time) datetime64[ns] 2019-01-2...\n",
+       "  * range                                     (range) float32 0.0 ... 1e+05\n",
+       "    azimuth                                   (time) float32 dask.array<chunksize=(6480,), meta=np.ndarray>\n",
+       "    elevation                                 (time) float32 dask.array<chunksize=(6480,), meta=np.ndarray>\n",
+       "Dimensions without coordinates: sweep\n",
+       "Data variables: (12/49)\n",
+       "    base_time                                 (time) datetime64[ns] 2019-01-2...\n",
+       "    time_offset                               (time) datetime64[ns] 2019-01-2...\n",
+       "    reflectivity                              (time, range) float32 dask.array<chunksize=(6480, 1001), meta=np.ndarray>\n",
+       "    cross_correlation_ratio_hv                (time, range) float32 dask.array<chunksize=(6480, 1001), meta=np.ndarray>\n",
+       "    normalized_coherent_power                 (time, range) float32 dask.array<chunksize=(6480, 1001), meta=np.ndarray>\n",
+       "    mean_doppler_velocity                     (time, range) float32 dask.array<chunksize=(6480, 1001), meta=np.ndarray>\n",
+       "    ...                                        ...\n",
+       "    path_integrated_attenuation               (time, range) float32 dask.array<chunksize=(6480, 1001), meta=np.ndarray>\n",
+       "    corrected_differential_reflectivity       (time, range) float32 dask.array<chunksize=(6480, 1001), meta=np.ndarray>\n",
+       "    ground_clutter                            (time, range) float32 dask.array<chunksize=(6480, 1001), meta=np.ndarray>\n",
+       "    lat                                       (time) float32 36.58 ... 36.58\n",
+       "    lon                                       (time) float32 -97.36 ... -97.36\n",
+       "    alt                                       (time) float32 330.0 ... 330.0\n",
+       "Attributes: (12/26)\n",
+       "    Conventions:           ARM-1.0 CF/Radial instrument_parameters\n",
+       "    title:                 Atmospheric Radiation Measurement (ARM) program X-...\n",
+       "    institution:           United States Department of Energy - Atmospheric R...\n",
+       "    references:            See XSAPR Instrument Handbook\n",
+       "    source:                Atmospheric Radiation Measurement (ARM) program X-...\n",
+       "    comment:               Data in this file has not be calibrated, corrected...\n",
+       "    ...                    ...\n",
+       "    original_container:    sigmet\n",
+       "    history:               created by user rjackson on machine or-condo-c215....\n",
+       "    _file_dates:           ['20190122', '20190122', '20190122', '20190122', '...\n",
+       "    _file_times:           ['001346', '003457', '005559', '011706', '013821',...\n",
+       "    _datastream:           sgpadicmac2I4.c1\n",
+       "    _arm_standards_flag:   1
" + ], + "text/plain": [ + "\n", + "Dimensions: (time: 97200, range: 1001,\n", + " sweep: 18)\n", + "Coordinates:\n", + " * time (time) datetime64[ns] 2019-01-2...\n", + " * range (range) float32 0.0 ... 1e+05\n", + " azimuth (time) float32 dask.array\n", + " elevation (time) float32 dask.array\n", + "Dimensions without coordinates: sweep\n", + "Data variables: (12/49)\n", + " base_time (time) datetime64[ns] 2019-01-2...\n", + " time_offset (time) datetime64[ns] 2019-01-2...\n", + " reflectivity (time, range) float32 dask.array\n", + " cross_correlation_ratio_hv (time, range) float32 dask.array\n", + " normalized_coherent_power (time, range) float32 dask.array\n", + " mean_doppler_velocity (time, range) float32 dask.array\n", + " ... ...\n", + " path_integrated_attenuation (time, range) float32 dask.array\n", + " corrected_differential_reflectivity (time, range) float32 dask.array\n", + " ground_clutter (time, range) float32 dask.array\n", + " lat (time) float32 36.58 ... 36.58\n", + " lon (time) float32 -97.36 ... -97.36\n", + " alt (time) float32 330.0 ... 330.0\n", + "Attributes: (12/26)\n", + " Conventions: ARM-1.0 CF/Radial instrument_parameters\n", + " title: Atmospheric Radiation Measurement (ARM) program X-...\n", + " institution: United States Department of Energy - Atmospheric R...\n", + " references: See XSAPR Instrument Handbook\n", + " source: Atmospheric Radiation Measurement (ARM) program X-...\n", + " comment: Data in this file has not be calibrated, corrected...\n", + " ... ...\n", + " original_container: sigmet\n", + " history: created by user rjackson on machine or-condo-c215....\n", + " _file_dates: ['20190122', '20190122', '20190122', '20190122', '...\n", + " _file_times: ['001346', '003457', '005559', '011706', '013821',...\n", + " _datastream: sgpadicmac2I4.c1\n", + " _arm_standards_flag: 1" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['reflectivity', 'cross_correlation_ratio_hv', 'normalized_coherent_power']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'reflectivity'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/CMAC2/CMAC2_tutorial.ipynb b/VAPs/quicklook/CMAC2/CMAC2_tutorial.ipynb new file mode 100644 index 00000000..144d0787 --- /dev/null +++ b/VAPs/quicklook/CMAC2/CMAC2_tutorial.ipynb @@ -0,0 +1,838 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# CMAC2.C1 Notebook\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/cmac2) for more information about this vap." + ] + }, + { + "cell_type": "markdown", + "id": "97097763", + "metadata": {}, + "source": [ + "In this notebook, we demonstrate the workflow to explore ARM vap data (using cmac2 as an example.) Value-added products (VAPs) are higher-order data products that have been analyzed and processed to ease scientists’ use of ARM data in atmospheric research and global climate models. \n", + "Here is the main content we will cover." + ] + }, + { + "cell_type": "markdown", + "id": "eddec40f", + "metadata": {}, + "source": [ + "# Table of Content\n", + "## Access the data\n", + "* How to retrieve the data\n", + "* Data path and file name conventions\n", + "* Load data\n", + "## Explore the data\n", + "* NetCDF Data structure\n", + "* Xarray essentials\n", + "* Xarray Variable\n", + "## Plot the data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "import random\n", + "\n", + "import glob\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "2372d5be", + "metadata": {}, + "source": [ + "## Access the data" + ] + }, + { + "cell_type": "markdown", + "id": "5b0d9684", + "metadata": {}, + "source": [ + "### How to retrieve the data\n", + "We assume the path \"/data/archive\" is available where you are running this notebook. You can use `os.path.exists(\"/data/archive\")` to verify if the path exists at your machine.\n", + "\n", + "\n", + "### Data path and file name conventions\n", + "There several common terminologies regarding ARM data, for example, data-stream-name, data-level, site, facility, instrument, etc. (For more details, please see [ARM Data File Standards Version 1.2](https://www.google.com/search?q=arm+datastream+facility+cite+definition&rlz=1C1GCEJ_enUS1029US1029&ei=hb41ZICrBPukqtsPvMODIA&ved=0ahUKEwjAgeiS0aL-AhV7kmoFHbzhAAQQ4dUDCBA&uact=5&oq=arm+datastream+facility+cite+definition&gs_lcp=Cgxnd3Mtd2l6LXNlcnAQAzIFCAAQogQyBQgAEKIEMgUIABCiBDIFCAAQogQyBQgAEKIEOgoIABBHENYEELADOgoIIRCgARDDBBAKSgQIQRgAUJ4DWIoOYO0PaAFwAXgAgAGMAYgBuQmSAQMyLjmYAQCgAQHIAQjAAQE&sclient=gws-wiz-serp).) \n", + "\n", + "For example, this notebook is called `cmac2.c1`, where `cmac2` is the \"datastream name\", and the `{process.ds_class_level}}` is the \"data level\".\n", + "\n", + "This datastream also contains site `sgp` and facility `I4`. (Note: individual datastream might have multiple site-facility pairs.)\n", + "In such a case, the data of this data-stream is stored at `/data/archive/sgp/sgpcmac2I4.c1`, which is in the format of `//.`. We can use the following method to assign the data-stream directory `datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )`\n", + "\n", + "The data files under datastream_dir also follows naming conventions. But once reach the datastream_dir level, the most import file naming convention to differentiate the files is \"yyyyMMdd.hhmmss\", which comes handy to filter out files based on datetime. For example, we can use `glob.glob(f'{datastream_dir}/*.200709*.*')` to filter files in 2007 September.\n", + "\n", + "(Note: refer to the https://adc.arm.gov/discovery/#/ and https://adc.arm.gov/solr8/metadata/select API to explore ARM datastream and assoicated available sites and facilities\n", + "\n", + "Please see the following examples in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d7e9eb85", + "metadata": {}, + "outputs": [], + "source": [ + "# Verify if DATA_DIR path exists\n", + "DATA_DIR = \"/data/archive\"\n", + "os.path.exists(DATA_DIR)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "586993fd", + "metadata": {}, + "outputs": [], + "source": [ + "# Speicify datastream_dir following the path conventions and check its existence\n", + "DATASTREAM_NAME = \"cmac2\"\n", + "DATA_LEVEL = \"c1\"\n", + "site = \"sgp\"\n", + "facility = \"I4\"\n", + "datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )\n", + "print(datastream_dir)\n", + "print(os.path.exists(datastream_dir))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0742f7c1", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: list 5 (random) files under datastream_dir\n", + "files = os.listdir(datastream_dir)\n", + "files[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39b98a36", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: get most recent file\n", + "list_of_files = glob.glob(f\"{datastream_dir}/*\") # * means all if need specific format then *.csv\n", + "latest_file = max(list_of_files, key=os.path.getctime)\n", + "latest_file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "902d514e", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus sort datastream files based on datetime\n", + "files = os.listdir(datastream_dir)\n", + "file_sorted = files.copy()\n", + "file_sorted.sort() \n", + "print(file_sorted[:5])\n", + "\n", + "# to reverse\n", + "file_sorted_reverse = files.copy() \n", + "file_sorted_reverse.sort(reverse=True)\n", + "print(file_sorted_reverse[:5])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec5923b2", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: pattern matching\n", + "# filter the 200709** files under datastream_dir\n", + "files_filter = glob.glob(f'{datastream_dir}/*.200709*.*')\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c4a4aa18", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: use armnotebook_utils.file_filter (TODO) to filter files based on datastream info \n" + ] + }, + { + "cell_type": "markdown", + "id": "cfeb9efc", + "metadata": {}, + "source": [ + "### Load data\n", + "The arm data is stored in [NetCDF](https://en.wikipedia.org/wiki/NetCDF#:~:text=%22NetCDF%20(network%20Common%20Data%20Form,format%20for%20representing%20scientific%20data.) format. We can use xarray's `open_dataset` method to load single file, or `open_mfdataset` to open multiple files. (Note: the latter will still return a single xarray dataset object by combining multiple files.)\n", + "\n", + "Note: open_dataset keeps the file handle open and lazy loads its contents. All parameters are passed directly to open_dataset. It is a preferable method over load_dataset for memory-effiency. (more details, please see [xarray.open_dataset](https://docs.xarray.dev/en/stable/generated/xarray.open_dataset.html))\n", + "\n", + "See the following example in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a440a329", + "metadata": {}, + "outputs": [], + "source": [ + "# open single file\n", + "full_path = latest_file\n", + "print(full_path)\n", + "ds_single = xr.open_dataset(full_path)\n", + "ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0143a3d", + "metadata": {}, + "outputs": [], + "source": [ + "print(ds_single)\n", + "print(type(ds_single))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c0f8939", + "metadata": {}, + "outputs": [], + "source": [ + "# open multiple files\n", + "n_files = 3\n", + "full_paths = [os.path.join(datastream_dir, f_path) for f_path in file_sorted_reverse[:n_files]]\n", + "print(full_paths)\n", + "try: # Note: sometimes multiple files cannot be merged, so we used try except here.\n", + " ds_mutiple = xr.open_mfdataset(full_paths)\n", + " # ds_mutiple\n", + " print(type(ds_mutiple))\n", + " print(ds_mutiple)\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "3fa59943", + "metadata": {}, + "source": [ + "## Explore the data" + ] + }, + { + "cell_type": "markdown", + "id": "571f69b9", + "metadata": {}, + "source": [ + "### NetCDF Data structure \n", + "\n", + "\n", + "(If you are confident with NetCDF basics and xarray essentials, feel free to skip this session.)\n", + "Before we dive into data exploration, there are some eseential concpets we should be familiar with (shown below): \n", + "* Dataset\n", + "* Data array\n", + "* Variable\n", + "* Dimenssion\n", + "* Coordinate\n", + "* Data Type\n", + "* Meta Data (Attributes)\n", + "\n", + "We will not go into details about NetCDF basics and here are some references you might find helpful\n", + "* [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#)\n", + "* [Network Common Data Form (NetCDF)](https://www.unidata.ucar.edu/software/netcdf/) and [A Brief History of (netCDF) Time](https://www.unidata.ucar.edu/software/netcdf/time/recs.html)\n", + "\n", + "\n", + "\n", + "### Xarray essentials\n", + "Earlier we introduced the xarray pacakge and used `open_dataset` and `open_mfdataset` to retrieve NetCDF data file as an xarray Dataset object. Recall that you can review the data in a notebook by using `print(ds)` or `ds`. \n", + "\n", + "Using xarray to retrieve the aforementioned NetCDF basics is straightforwared. In general,\n", + "* Dataset: `ds`\n", + "* Data array: `ds.variables`\n", + "* Variable: `ds.ds.variables`\n", + "* Dimenssion: `ds.dims`\n", + "* Coordinate: `ds.coords`\n", + "* Data Type: `type`\n", + "* Meta Data (Attributes)\n", + "\n", + "Also, here are some references if you are new to xarray\n", + "* [Xarray in 45 minutes](https://tutorial.xarray.dev/overview/xarray-in-45-min.html)\n", + "* [Handling NetCDF Files using XArray for Absolute Beginners](https://towardsdatascience.com/handling-netcdf-files-using-xarray-for-absolute-beginners-111a8ab4463f)\n", + "\n", + "Try the following commands in action\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b3eac323", + "metadata": {}, + "outputs": [], + "source": [ + "ds = ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77ecf85d", + "metadata": {}, + "outputs": [], + "source": [ + "# Dataset \n", + "ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25e7de09", + "metadata": {}, + "outputs": [], + "source": [ + "# Data array, Variable\n", + "# Note: the info can be overwhelming. Do not attempt to grasp everything at the first glance.\n", + "ds.variables" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c41b67e", + "metadata": {}, + "outputs": [], + "source": [ + "# dimenssions\n", + "ds.dims" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "156f1dfc", + "metadata": {}, + "outputs": [], + "source": [ + "# coordinates\n", + "ds.coords" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "277d6064", + "metadata": {}, + "outputs": [], + "source": [ + "# Meta Data (Attributes)\n", + "ds.attrs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d334681f", + "metadata": {}, + "outputs": [], + "source": [ + "# type\n", + "print(type(ds))\n", + "print(type(ds.variables))\n", + "print(type(ds.dims))\n", + "print(type(ds.coords))\n", + "print(type(ds.attrs))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "643399d6", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: retrieve variable names only\n", + "list(ds.variables)" + ] + }, + { + "cell_type": "markdown", + "id": "1cf36878", + "metadata": {}, + "source": [ + "#### Discussion: variable vs. coordinates vs. dimenssions. \n", + "From [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#):\n", + "\n", + "* Dimession: A dimension may be used to represent a real physical dimension, for example, time, latitude, longitude, or height. A dimension might also be used to index other quantities, for example station or model-run-number.\n", + "A netCDF dimension has both a name and a length. A dimension length is an arbitrary positive integer, except that one dimension in a netCDF dataset can have the length UNLIMITED.\n", + "\n", + "* Variables: Variables are used to store the bulk of the data in a netCDF dataset. A variable represents an array of values of the same type. A scalar value is treated as a 0-dimensional array. A variable has a name, a data type, and a shape described by its list of dimensions specified when the variable is created. A variable may also have associated attributes, which may be added, deleted or changed after the variable is created.\n", + "\n", + "* Coordinate (Variables): It is legal for a variable to have the same name as a dimension. Such variables have no special meaning to the netCDF library. However there is a convention that such variables should be treated in a special way by software using this library.\n", + "A variable with the same name as a dimension is called a coordinate variable. It typically defines a physical coordinate corresponding to that dimension.\n", + "\n", + "\n", + "Tips: By definition, coordinates and dimenssions are also variable. For example ds.time is a coordinate but it is also a special variable. But in practice, when we talk about variable, it implies regular/non-coordinate variable. We will use this convention for the remaining notebook. " + ] + }, + { + "cell_type": "markdown", + "id": "d47120f9", + "metadata": {}, + "source": [ + "### Xarray Variable\n", + "\n", + "\n", + "We can use `ds.variables` to access varialbes of a dataset. But many times viewing print-out the whole datasets can be overwhemling. Instead, we would work on the individual variable (also called Xarray data array.) using the `ds[var_name]` syntax, where `var_name` is the name of the variable. See the following examples in action.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5a57e136", + "metadata": {}, + "outputs": [], + "source": [ + "print(type(ds[\"time\"]))\n", + "ds[\"time\"]" + ] + }, + { + "cell_type": "markdown", + "id": "51ec643e", + "metadata": {}, + "source": [ + "#### Variable properties\n", + "For individual varible, the following properties are mostly used with the assocaited xarray method to retrieve them. (assuming var = ds[var_name])\n", + "* name: `var.name`\n", + "* data content: `var.data`\n", + "* attributes: `var.attrs`\n", + "* dimenstions: `var.dims`\n", + "* data type: `var.data.dtype`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a632a525", + "metadata": {}, + "outputs": [], + "source": [ + "var_name = \"time\"\n", + "var = ds[var_name]\n", + "\n", + "print(\"var.name: \\n\", var.name, \"\\n\")\n", + "print(\"var.data: \\n\", var.data, \"\\n\")\n", + "print(\"var.attrs: \\n\", var.attrs, \"\\n\")\n", + "print(\"var.dims: \\n\", var.dims, \"\\n\")\n", + "print(\"var.data.dtype: \\n\", var.data.dtype, \"\\n\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b256b07f", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: indivdual variable info table (collect and display variable info as a tabular format)\n", + "df_info = pd.DataFrame()\n", + "df_info[\"var_name\"] = list(ds.variables)\n", + "df_info[\"dims\"] = df_info.var_name.apply(lambda x: ds[x].dims)\n", + "df_info[\"is_dim\"] = df_info.var_name.apply(lambda x: x in ds.dims)\n", + "df_info[\"n_dim\"] = df_info.var_name.apply(lambda x: len(ds[x].dims))\n", + "df_info[\"attrs\"] = df_info.var_name.apply(lambda x: ds[x].attrs)\n", + "df_info[\"dtype\"] = df_info.var_name.apply(lambda x: ds[x].data.dtype)\n", + "df_info" + ] + }, + { + "cell_type": "markdown", + "id": "e6a36b96", + "metadata": {}, + "source": [ + "### Data cleaning/Preprocessing (skipped)\n", + "Data cleaning and preprocessing is an important stage in the the data analysis pipeline. However it will be out of the scope of this notebook. For more information about data cleaning and preprocessing basics, here are some references. \n", + "* [Xarray Fundamentals](https://earth-env-data-science.github.io/lectures/xarray/xarray.html)\n", + "* [Xarray Tutorial — Pangeo Gallery documentation](http://gallery.pangeo.io/repos/pangeo-data/pangeo-tutorial-gallery/xarray.html)\n", + "* [Pythonic Data Cleaning With pandas and NumPy](https://realpython.com/python-data-cleaning-numpy-pandas/)\n", + "* [Pandas - Cleaning Data](https://www.w3schools.com/python/pandas/pandas_cleaning.asp)" + ] + }, + { + "cell_type": "markdown", + "id": "ae6e1a69", + "metadata": {}, + "source": [ + "### Plotting\n", + "Data visualization (or plotting) is another important data analysis topic and deserves its own discussion. Here in this notebook we will only demonstrate basic tool for simple data visualization tasks.\n", + "\n", + "Here are some reference you might find useful:\n", + "* xarray plotting: https://docs.xarray.dev/en/stable/user-guide/plotting.html\n", + "* Atmospheric Community Toolkit (ACT): https://arm-doe.github.io/ACT/index.html\n", + "\n", + "Note: this notebook is auto-generated using a template. It uses a general idea to select variable(s) to plot and is not customized to each indivdual notebook. Feel free to change the variables in intrrests, especially certain figure is failed to plot." + ] + }, + { + "cell_type": "markdown", + "id": "7e417fb2", + "metadata": {}, + "source": [ + "#### 1-dimenssional basic time series plot\n", + "\n", + "For the following plot we would like to find variables such that\n", + "* it has one and only one dimession\n", + "* \"time\" is its coordinate variable\n", + "* it is not a dimenssion itself,\n", + "* it is not a special variable with substrings within [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "\n", + "Please see the following example in action. (For more details about pandas filtering, please see the following references.)\n", + "* [pandas: multiple conditions while indexing data frame](https://stackoverflow.com/questions/22591174/pandas-multiple-conditions-while-indexing-data-frame-unexpected-behavior)\n", + "* [pandas.DataFrame.apply](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.apply.html)\n", + "* [How to test if a string contains one of the substrings in a list...](https://stackoverflow.com/questions/26577516/how-to-test-if-a-string-contains-one-of-the-substrings-in-a-list-in-pandas" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30edac2d", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==1, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter = df_info[(df_info.n_dim == 1) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4777a659", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_1d = df_filter.var_name.values[0]\n", + " var_1d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb5ae985", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " ds[var_1d].plot()\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "9e41066e", + "metadata": {}, + "source": [ + "#### 2-dimenssional basic plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09bd4adc", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==2, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_2 = df_info[(df_info.n_dim == 2) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2cdea1a9", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_2d = df_filter_2.var_name.values[0]\n", + " var_2d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5f48879", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " print(ds[var_2d].dims)\n", + " # ds[var_2d].plot()\n", + "\n", + " # conventionally, use \"time\" as x-axis\n", + " ds[var_2d].plot(x=\"time\")\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "7554b2a6", + "metadata": {}, + "source": [ + "#### qc-plotting (optional)\n", + "\n", + "Note: act qc plotting has more strict requirement, one of them is the associated qc variable need to have \"flag_masks\" attributes. Which is added by using ds = act.io.armfiles.read_netcdf(files_list), then ds.clean.cleanup()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71c2096f", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " ds_act = act.io.armfiles.read_netcdf(full_path)\n", + " print(type(ds_act))\n", + " ds_act.clean.cleanup()\n", + "\n", + " # or \n", + " # ds.clean.cleanup()\n", + "except Exception as e:\n", + " print(\"ERROR\", e)\n", + " ds_act = ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "844f8505", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter valid variables for ACT qc plotting\n", + "condition = (\"qc_\" + df_info.var_name).apply(lambda x: ds_act[x].attrs.get(\"flag_masks\") is not None \n", + " if x in list(ds.variables) else False)\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_3 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings))) &\n", + " condition\n", + " ]\n", + "df_filter_3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd38cc6", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "try:\n", + " qc_variable = df_filter_3.var_name.values[0]\n", + " print(qc_variable)\n", + "except Exception as e:\n", + " print(e)\n", + " \n", + "try:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + " qc_ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "d76e4d27", + "metadata": {}, + "source": [ + "#### bonus: choose variables to plot from a dropdown menu " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb733804", + "metadata": {}, + "outputs": [], + "source": [ + "# Valid variables filtering\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_4 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82d5c20d", + "metadata": {}, + "outputs": [], + "source": [ + "# example 1: using xarray plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2fc9aaa4", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# fig, ax = plt.subplots(figsize=(10, 4))\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "# fig.clear() # Remove old lines from plot and plot new one\n", + "# if len(ds[var].dims)==2:\n", + "# ds[var].plot(x=\"time\", add_colorbar=False)\n", + "# else:\n", + "# ds[var].plot()\n", + "# plt.grid()\n", + "# plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fabdd802", + "metadata": {}, + "outputs": [], + "source": [ + "# example 2: using act plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "962b4186", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "\n", + "\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "\n", + "# i_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + "# i_display.add_subplots((1,), figsize=(10, 4))\n", + "# ax = i_display.plot(var, subplot_index=(0,), set_title=f\"{var} ({ds_act[var].attrs['long_name']})\",)\n", + "\n", + "# ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "# ax.grid()\n", + "# plt.show()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/CMAC2/cmac2.c1.ipynb b/VAPs/quicklook/CMAC2/cmac2.c1.ipynb new file mode 100644 index 00000000..5a555298 --- /dev/null +++ b/VAPs/quicklook/CMAC2/cmac2.c1.ipynb @@ -0,0 +1,297 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# CMAC2.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/cmac2) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'cmac2'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2019-01-22', 'facility': 'I4', 'site': 'sgp', 'start_date': '2018-08-30'}, {'end_date': '2019-04-05', 'facility': 'I5', 'site': 'sgp', 'start_date': '2018-08-30'}, {'end_date': '2019-02-26', 'facility': 'I6', 'site': 'sgp', 'start_date': '2018-08-30'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'I4' )\n", + "\n", + "date_start = '2019-01-21'\n", + "date_end = '2019-01-22'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['reflectivity', 'cross_correlation_ratio_hv', 'normalized_coherent_power']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'reflectivity'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/CO-AIR/CO-AIR_tutorial.ipynb b/VAPs/quicklook/CO-AIR/CO-AIR_tutorial.ipynb new file mode 100644 index 00000000..4c6b0624 --- /dev/null +++ b/VAPs/quicklook/CO-AIR/CO-AIR_tutorial.ipynb @@ -0,0 +1,838 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# AAFCO.C1 Notebook\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/co-air) for more information about this vap." + ] + }, + { + "cell_type": "markdown", + "id": "97097763", + "metadata": {}, + "source": [ + "In this notebook, we demonstrate the workflow to explore ARM vap data (using aafco as an example.) Value-added products (VAPs) are higher-order data products that have been analyzed and processed to ease scientists’ use of ARM data in atmospheric research and global climate models. \n", + "Here is the main content we will cover." + ] + }, + { + "cell_type": "markdown", + "id": "eddec40f", + "metadata": {}, + "source": [ + "# Table of Content\n", + "## Access the data\n", + "* How to retrieve the data\n", + "* Data path and file name conventions\n", + "* Load data\n", + "## Explore the data\n", + "* NetCDF Data structure\n", + "* Xarray essentials\n", + "* Xarray Variable\n", + "## Plot the data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "import random\n", + "\n", + "import glob\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "2372d5be", + "metadata": {}, + "source": [ + "## Access the data" + ] + }, + { + "cell_type": "markdown", + "id": "5b0d9684", + "metadata": {}, + "source": [ + "### How to retrieve the data\n", + "We assume the path \"/data/archive\" is available where you are running this notebook. You can use `os.path.exists(\"/data/archive\")` to verify if the path exists at your machine.\n", + "\n", + "\n", + "### Data path and file name conventions\n", + "There several common terminologies regarding ARM data, for example, data-stream-name, data-level, site, facility, instrument, etc. (For more details, please see [ARM Data File Standards Version 1.2](https://www.google.com/search?q=arm+datastream+facility+cite+definition&rlz=1C1GCEJ_enUS1029US1029&ei=hb41ZICrBPukqtsPvMODIA&ved=0ahUKEwjAgeiS0aL-AhV7kmoFHbzhAAQQ4dUDCBA&uact=5&oq=arm+datastream+facility+cite+definition&gs_lcp=Cgxnd3Mtd2l6LXNlcnAQAzIFCAAQogQyBQgAEKIEMgUIABCiBDIFCAAQogQyBQgAEKIEOgoIABBHENYEELADOgoIIRCgARDDBBAKSgQIQRgAUJ4DWIoOYO0PaAFwAXgAgAGMAYgBuQmSAQMyLjmYAQCgAQHIAQjAAQE&sclient=gws-wiz-serp).) \n", + "\n", + "For example, this notebook is called `aafco.c1`, where `aafco` is the \"datastream name\", and the `{process.ds_class_level}}` is the \"data level\".\n", + "\n", + "This datastream also contains site `cor` and facility `F1`. (Note: individual datastream might have multiple site-facility pairs.)\n", + "In such a case, the data of this data-stream is stored at `/data/archive/cor/coraafcoF1.c1`, which is in the format of `//.`. We can use the following method to assign the data-stream directory `datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )`\n", + "\n", + "The data files under datastream_dir also follows naming conventions. But once reach the datastream_dir level, the most import file naming convention to differentiate the files is \"yyyyMMdd.hhmmss\", which comes handy to filter out files based on datetime. For example, we can use `glob.glob(f'{datastream_dir}/*.200709*.*')` to filter files in 2007 September.\n", + "\n", + "(Note: refer to the https://adc.arm.gov/discovery/#/ and https://adc.arm.gov/solr8/metadata/select API to explore ARM datastream and assoicated available sites and facilities\n", + "\n", + "Please see the following examples in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d7e9eb85", + "metadata": {}, + "outputs": [], + "source": [ + "# Verify if DATA_DIR path exists\n", + "DATA_DIR = \"/data/archive\"\n", + "os.path.exists(DATA_DIR)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "586993fd", + "metadata": {}, + "outputs": [], + "source": [ + "# Speicify datastream_dir following the path conventions and check its existence\n", + "DATASTREAM_NAME = \"aafco\"\n", + "DATA_LEVEL = \"c1\"\n", + "site = \"cor\"\n", + "facility = \"F1\"\n", + "datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )\n", + "print(datastream_dir)\n", + "print(os.path.exists(datastream_dir))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0742f7c1", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: list 5 (random) files under datastream_dir\n", + "files = os.listdir(datastream_dir)\n", + "files[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39b98a36", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: get most recent file\n", + "list_of_files = glob.glob(f\"{datastream_dir}/*\") # * means all if need specific format then *.csv\n", + "latest_file = max(list_of_files, key=os.path.getctime)\n", + "latest_file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "902d514e", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus sort datastream files based on datetime\n", + "files = os.listdir(datastream_dir)\n", + "file_sorted = files.copy()\n", + "file_sorted.sort() \n", + "print(file_sorted[:5])\n", + "\n", + "# to reverse\n", + "file_sorted_reverse = files.copy() \n", + "file_sorted_reverse.sort(reverse=True)\n", + "print(file_sorted_reverse[:5])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec5923b2", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: pattern matching\n", + "# filter the 200709** files under datastream_dir\n", + "files_filter = glob.glob(f'{datastream_dir}/*.200709*.*')\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c4a4aa18", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: use armnotebook_utils.file_filter (TODO) to filter files based on datastream info \n" + ] + }, + { + "cell_type": "markdown", + "id": "cfeb9efc", + "metadata": {}, + "source": [ + "### Load data\n", + "The arm data is stored in [NetCDF](https://en.wikipedia.org/wiki/NetCDF#:~:text=%22NetCDF%20(network%20Common%20Data%20Form,format%20for%20representing%20scientific%20data.) format. We can use xarray's `open_dataset` method to load single file, or `open_mfdataset` to open multiple files. (Note: the latter will still return a single xarray dataset object by combining multiple files.)\n", + "\n", + "Note: open_dataset keeps the file handle open and lazy loads its contents. All parameters are passed directly to open_dataset. It is a preferable method over load_dataset for memory-effiency. (more details, please see [xarray.open_dataset](https://docs.xarray.dev/en/stable/generated/xarray.open_dataset.html))\n", + "\n", + "See the following example in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a440a329", + "metadata": {}, + "outputs": [], + "source": [ + "# open single file\n", + "full_path = latest_file\n", + "print(full_path)\n", + "ds_single = xr.open_dataset(full_path)\n", + "ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0143a3d", + "metadata": {}, + "outputs": [], + "source": [ + "print(ds_single)\n", + "print(type(ds_single))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c0f8939", + "metadata": {}, + "outputs": [], + "source": [ + "# open multiple files\n", + "n_files = 3\n", + "full_paths = [os.path.join(datastream_dir, f_path) for f_path in file_sorted_reverse[:n_files]]\n", + "print(full_paths)\n", + "try: # Note: sometimes multiple files cannot be merged, so we used try except here.\n", + " ds_mutiple = xr.open_mfdataset(full_paths)\n", + " # ds_mutiple\n", + " print(type(ds_mutiple))\n", + " print(ds_mutiple)\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "3fa59943", + "metadata": {}, + "source": [ + "## Explore the data" + ] + }, + { + "cell_type": "markdown", + "id": "571f69b9", + "metadata": {}, + "source": [ + "### NetCDF Data structure \n", + "\n", + "\n", + "(If you are confident with NetCDF basics and xarray essentials, feel free to skip this session.)\n", + "Before we dive into data exploration, there are some eseential concpets we should be familiar with (shown below): \n", + "* Dataset\n", + "* Data array\n", + "* Variable\n", + "* Dimenssion\n", + "* Coordinate\n", + "* Data Type\n", + "* Meta Data (Attributes)\n", + "\n", + "We will not go into details about NetCDF basics and here are some references you might find helpful\n", + "* [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#)\n", + "* [Network Common Data Form (NetCDF)](https://www.unidata.ucar.edu/software/netcdf/) and [A Brief History of (netCDF) Time](https://www.unidata.ucar.edu/software/netcdf/time/recs.html)\n", + "\n", + "\n", + "\n", + "### Xarray essentials\n", + "Earlier we introduced the xarray pacakge and used `open_dataset` and `open_mfdataset` to retrieve NetCDF data file as an xarray Dataset object. Recall that you can review the data in a notebook by using `print(ds)` or `ds`. \n", + "\n", + "Using xarray to retrieve the aforementioned NetCDF basics is straightforwared. In general,\n", + "* Dataset: `ds`\n", + "* Data array: `ds.variables`\n", + "* Variable: `ds.ds.variables`\n", + "* Dimenssion: `ds.dims`\n", + "* Coordinate: `ds.coords`\n", + "* Data Type: `type`\n", + "* Meta Data (Attributes)\n", + "\n", + "Also, here are some references if you are new to xarray\n", + "* [Xarray in 45 minutes](https://tutorial.xarray.dev/overview/xarray-in-45-min.html)\n", + "* [Handling NetCDF Files using XArray for Absolute Beginners](https://towardsdatascience.com/handling-netcdf-files-using-xarray-for-absolute-beginners-111a8ab4463f)\n", + "\n", + "Try the following commands in action\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b3eac323", + "metadata": {}, + "outputs": [], + "source": [ + "ds = ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77ecf85d", + "metadata": {}, + "outputs": [], + "source": [ + "# Dataset \n", + "ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25e7de09", + "metadata": {}, + "outputs": [], + "source": [ + "# Data array, Variable\n", + "# Note: the info can be overwhelming. Do not attempt to grasp everything at the first glance.\n", + "ds.variables" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c41b67e", + "metadata": {}, + "outputs": [], + "source": [ + "# dimenssions\n", + "ds.dims" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "156f1dfc", + "metadata": {}, + "outputs": [], + "source": [ + "# coordinates\n", + "ds.coords" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "277d6064", + "metadata": {}, + "outputs": [], + "source": [ + "# Meta Data (Attributes)\n", + "ds.attrs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d334681f", + "metadata": {}, + "outputs": [], + "source": [ + "# type\n", + "print(type(ds))\n", + "print(type(ds.variables))\n", + "print(type(ds.dims))\n", + "print(type(ds.coords))\n", + "print(type(ds.attrs))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "643399d6", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: retrieve variable names only\n", + "list(ds.variables)" + ] + }, + { + "cell_type": "markdown", + "id": "1cf36878", + "metadata": {}, + "source": [ + "#### Discussion: variable vs. coordinates vs. dimenssions. \n", + "From [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#):\n", + "\n", + "* Dimession: A dimension may be used to represent a real physical dimension, for example, time, latitude, longitude, or height. A dimension might also be used to index other quantities, for example station or model-run-number.\n", + "A netCDF dimension has both a name and a length. A dimension length is an arbitrary positive integer, except that one dimension in a netCDF dataset can have the length UNLIMITED.\n", + "\n", + "* Variables: Variables are used to store the bulk of the data in a netCDF dataset. A variable represents an array of values of the same type. A scalar value is treated as a 0-dimensional array. A variable has a name, a data type, and a shape described by its list of dimensions specified when the variable is created. A variable may also have associated attributes, which may be added, deleted or changed after the variable is created.\n", + "\n", + "* Coordinate (Variables): It is legal for a variable to have the same name as a dimension. Such variables have no special meaning to the netCDF library. However there is a convention that such variables should be treated in a special way by software using this library.\n", + "A variable with the same name as a dimension is called a coordinate variable. It typically defines a physical coordinate corresponding to that dimension.\n", + "\n", + "\n", + "Tips: By definition, coordinates and dimenssions are also variable. For example ds.time is a coordinate but it is also a special variable. But in practice, when we talk about variable, it implies regular/non-coordinate variable. We will use this convention for the remaining notebook. " + ] + }, + { + "cell_type": "markdown", + "id": "d47120f9", + "metadata": {}, + "source": [ + "### Xarray Variable\n", + "\n", + "\n", + "We can use `ds.variables` to access varialbes of a dataset. But many times viewing print-out the whole datasets can be overwhemling. Instead, we would work on the individual variable (also called Xarray data array.) using the `ds[var_name]` syntax, where `var_name` is the name of the variable. See the following examples in action.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5a57e136", + "metadata": {}, + "outputs": [], + "source": [ + "print(type(ds[\"time\"]))\n", + "ds[\"time\"]" + ] + }, + { + "cell_type": "markdown", + "id": "51ec643e", + "metadata": {}, + "source": [ + "#### Variable properties\n", + "For individual varible, the following properties are mostly used with the assocaited xarray method to retrieve them. (assuming var = ds[var_name])\n", + "* name: `var.name`\n", + "* data content: `var.data`\n", + "* attributes: `var.attrs`\n", + "* dimenstions: `var.dims`\n", + "* data type: `var.data.dtype`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a632a525", + "metadata": {}, + "outputs": [], + "source": [ + "var_name = \"time\"\n", + "var = ds[var_name]\n", + "\n", + "print(\"var.name: \\n\", var.name, \"\\n\")\n", + "print(\"var.data: \\n\", var.data, \"\\n\")\n", + "print(\"var.attrs: \\n\", var.attrs, \"\\n\")\n", + "print(\"var.dims: \\n\", var.dims, \"\\n\")\n", + "print(\"var.data.dtype: \\n\", var.data.dtype, \"\\n\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b256b07f", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: indivdual variable info table (collect and display variable info as a tabular format)\n", + "df_info = pd.DataFrame()\n", + "df_info[\"var_name\"] = list(ds.variables)\n", + "df_info[\"dims\"] = df_info.var_name.apply(lambda x: ds[x].dims)\n", + "df_info[\"is_dim\"] = df_info.var_name.apply(lambda x: x in ds.dims)\n", + "df_info[\"n_dim\"] = df_info.var_name.apply(lambda x: len(ds[x].dims))\n", + "df_info[\"attrs\"] = df_info.var_name.apply(lambda x: ds[x].attrs)\n", + "df_info[\"dtype\"] = df_info.var_name.apply(lambda x: ds[x].data.dtype)\n", + "df_info" + ] + }, + { + "cell_type": "markdown", + "id": "e6a36b96", + "metadata": {}, + "source": [ + "### Data cleaning/Preprocessing (skipped)\n", + "Data cleaning and preprocessing is an important stage in the the data analysis pipeline. However it will be out of the scope of this notebook. For more information about data cleaning and preprocessing basics, here are some references. \n", + "* [Xarray Fundamentals](https://earth-env-data-science.github.io/lectures/xarray/xarray.html)\n", + "* [Xarray Tutorial — Pangeo Gallery documentation](http://gallery.pangeo.io/repos/pangeo-data/pangeo-tutorial-gallery/xarray.html)\n", + "* [Pythonic Data Cleaning With pandas and NumPy](https://realpython.com/python-data-cleaning-numpy-pandas/)\n", + "* [Pandas - Cleaning Data](https://www.w3schools.com/python/pandas/pandas_cleaning.asp)" + ] + }, + { + "cell_type": "markdown", + "id": "ae6e1a69", + "metadata": {}, + "source": [ + "### Plotting\n", + "Data visualization (or plotting) is another important data analysis topic and deserves its own discussion. Here in this notebook we will only demonstrate basic tool for simple data visualization tasks.\n", + "\n", + "Here are some reference you might find useful:\n", + "* xarray plotting: https://docs.xarray.dev/en/stable/user-guide/plotting.html\n", + "* Atmospheric Community Toolkit (ACT): https://arm-doe.github.io/ACT/index.html\n", + "\n", + "Note: this notebook is auto-generated using a template. It uses a general idea to select variable(s) to plot and is not customized to each indivdual notebook. Feel free to change the variables in intrrests, especially certain figure is failed to plot." + ] + }, + { + "cell_type": "markdown", + "id": "7e417fb2", + "metadata": {}, + "source": [ + "#### 1-dimenssional basic time series plot\n", + "\n", + "For the following plot we would like to find variables such that\n", + "* it has one and only one dimession\n", + "* \"time\" is its coordinate variable\n", + "* it is not a dimenssion itself,\n", + "* it is not a special variable with substrings within [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "\n", + "Please see the following example in action. (For more details about pandas filtering, please see the following references.)\n", + "* [pandas: multiple conditions while indexing data frame](https://stackoverflow.com/questions/22591174/pandas-multiple-conditions-while-indexing-data-frame-unexpected-behavior)\n", + "* [pandas.DataFrame.apply](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.apply.html)\n", + "* [How to test if a string contains one of the substrings in a list...](https://stackoverflow.com/questions/26577516/how-to-test-if-a-string-contains-one-of-the-substrings-in-a-list-in-pandas" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30edac2d", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==1, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter = df_info[(df_info.n_dim == 1) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4777a659", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_1d = df_filter.var_name.values[0]\n", + " var_1d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb5ae985", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " ds[var_1d].plot()\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "9e41066e", + "metadata": {}, + "source": [ + "#### 2-dimenssional basic plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09bd4adc", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==2, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_2 = df_info[(df_info.n_dim == 2) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2cdea1a9", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_2d = df_filter_2.var_name.values[0]\n", + " var_2d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5f48879", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " print(ds[var_2d].dims)\n", + " # ds[var_2d].plot()\n", + "\n", + " # conventionally, use \"time\" as x-axis\n", + " ds[var_2d].plot(x=\"time\")\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "7554b2a6", + "metadata": {}, + "source": [ + "#### qc-plotting (optional)\n", + "\n", + "Note: act qc plotting has more strict requirement, one of them is the associated qc variable need to have \"flag_masks\" attributes. Which is added by using ds = act.io.armfiles.read_netcdf(files_list), then ds.clean.cleanup()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71c2096f", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " ds_act = act.io.armfiles.read_netcdf(full_path)\n", + " print(type(ds_act))\n", + " ds_act.clean.cleanup()\n", + "\n", + " # or \n", + " # ds.clean.cleanup()\n", + "except Exception as e:\n", + " print(\"ERROR\", e)\n", + " ds_act = ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "844f8505", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter valid variables for ACT qc plotting\n", + "condition = (\"qc_\" + df_info.var_name).apply(lambda x: ds_act[x].attrs.get(\"flag_masks\") is not None \n", + " if x in list(ds.variables) else False)\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_3 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings))) &\n", + " condition\n", + " ]\n", + "df_filter_3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd38cc6", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "try:\n", + " qc_variable = df_filter_3.var_name.values[0]\n", + " print(qc_variable)\n", + "except Exception as e:\n", + " print(e)\n", + " \n", + "try:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + " qc_ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "d76e4d27", + "metadata": {}, + "source": [ + "#### bonus: choose variables to plot from a dropdown menu " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb733804", + "metadata": {}, + "outputs": [], + "source": [ + "# Valid variables filtering\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_4 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82d5c20d", + "metadata": {}, + "outputs": [], + "source": [ + "# example 1: using xarray plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2fc9aaa4", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# fig, ax = plt.subplots(figsize=(10, 4))\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "# fig.clear() # Remove old lines from plot and plot new one\n", + "# if len(ds[var].dims)==2:\n", + "# ds[var].plot(x=\"time\", add_colorbar=False)\n", + "# else:\n", + "# ds[var].plot()\n", + "# plt.grid()\n", + "# plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fabdd802", + "metadata": {}, + "outputs": [], + "source": [ + "# example 2: using act plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "962b4186", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "\n", + "\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "\n", + "# i_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + "# i_display.add_subplots((1,), figsize=(10, 4))\n", + "# ax = i_display.plot(var, subplot_index=(0,), set_title=f\"{var} ({ds_act[var].attrs['long_name']})\",)\n", + "\n", + "# ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "# ax.grid()\n", + "# plt.show()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/CO-AIR/aafco.c1.ipynb b/VAPs/quicklook/CO-AIR/aafco.c1.ipynb new file mode 100644 index 00000000..d222d0fb --- /dev/null +++ b/VAPs/quicklook/CO-AIR/aafco.c1.ipynb @@ -0,0 +1,297 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# AAFCO.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/co-air) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'aafco'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2018-12-08', 'facility': 'F1', 'site': 'cor', 'start_date': '2018-11-04'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'cor', 'F1' )\n", + "\n", + "date_start = '2018-12-06'\n", + "date_end = '2018-12-08'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['co', 'n2o', 'h2o']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'co'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/COGS/COGS_tutorial.ipynb b/VAPs/quicklook/COGS/COGS_tutorial.ipynb new file mode 100644 index 00000000..461cee79 --- /dev/null +++ b/VAPs/quicklook/COGS/COGS_tutorial.ipynb @@ -0,0 +1,838 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# COGS.C1 Notebook\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/cogs) for more information about this vap." + ] + }, + { + "cell_type": "markdown", + "id": "97097763", + "metadata": {}, + "source": [ + "In this notebook, we demonstrate the workflow to explore ARM vap data (using cogs as an example.) Value-added products (VAPs) are higher-order data products that have been analyzed and processed to ease scientists’ use of ARM data in atmospheric research and global climate models. \n", + "Here is the main content we will cover." + ] + }, + { + "cell_type": "markdown", + "id": "eddec40f", + "metadata": {}, + "source": [ + "# Table of Content\n", + "## Access the data\n", + "* How to retrieve the data\n", + "* Data path and file name conventions\n", + "* Load data\n", + "## Explore the data\n", + "* NetCDF Data structure\n", + "* Xarray essentials\n", + "* Xarray Variable\n", + "## Plot the data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "import random\n", + "\n", + "import glob\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "2372d5be", + "metadata": {}, + "source": [ + "## Access the data" + ] + }, + { + "cell_type": "markdown", + "id": "5b0d9684", + "metadata": {}, + "source": [ + "### How to retrieve the data\n", + "We assume the path \"/data/archive\" is available where you are running this notebook. You can use `os.path.exists(\"/data/archive\")` to verify if the path exists at your machine.\n", + "\n", + "\n", + "### Data path and file name conventions\n", + "There several common terminologies regarding ARM data, for example, data-stream-name, data-level, site, facility, instrument, etc. (For more details, please see [ARM Data File Standards Version 1.2](https://www.google.com/search?q=arm+datastream+facility+cite+definition&rlz=1C1GCEJ_enUS1029US1029&ei=hb41ZICrBPukqtsPvMODIA&ved=0ahUKEwjAgeiS0aL-AhV7kmoFHbzhAAQQ4dUDCBA&uact=5&oq=arm+datastream+facility+cite+definition&gs_lcp=Cgxnd3Mtd2l6LXNlcnAQAzIFCAAQogQyBQgAEKIEMgUIABCiBDIFCAAQogQyBQgAEKIEOgoIABBHENYEELADOgoIIRCgARDDBBAKSgQIQRgAUJ4DWIoOYO0PaAFwAXgAgAGMAYgBuQmSAQMyLjmYAQCgAQHIAQjAAQE&sclient=gws-wiz-serp).) \n", + "\n", + "For example, this notebook is called `cogs.c1`, where `cogs` is the \"datastream name\", and the `{process.ds_class_level}}` is the \"data level\".\n", + "\n", + "This datastream also contains site `sgp` and facility `N1`. (Note: individual datastream might have multiple site-facility pairs.)\n", + "In such a case, the data of this data-stream is stored at `/data/archive/sgp/sgpcogsN1.c1`, which is in the format of `//.`. We can use the following method to assign the data-stream directory `datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )`\n", + "\n", + "The data files under datastream_dir also follows naming conventions. But once reach the datastream_dir level, the most import file naming convention to differentiate the files is \"yyyyMMdd.hhmmss\", which comes handy to filter out files based on datetime. For example, we can use `glob.glob(f'{datastream_dir}/*.200709*.*')` to filter files in 2007 September.\n", + "\n", + "(Note: refer to the https://adc.arm.gov/discovery/#/ and https://adc.arm.gov/solr8/metadata/select API to explore ARM datastream and assoicated available sites and facilities\n", + "\n", + "Please see the following examples in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d7e9eb85", + "metadata": {}, + "outputs": [], + "source": [ + "# Verify if DATA_DIR path exists\n", + "DATA_DIR = \"/data/archive\"\n", + "os.path.exists(DATA_DIR)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "586993fd", + "metadata": {}, + "outputs": [], + "source": [ + "# Speicify datastream_dir following the path conventions and check its existence\n", + "DATASTREAM_NAME = \"cogs\"\n", + "DATA_LEVEL = \"c1\"\n", + "site = \"sgp\"\n", + "facility = \"N1\"\n", + "datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )\n", + "print(datastream_dir)\n", + "print(os.path.exists(datastream_dir))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0742f7c1", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: list 5 (random) files under datastream_dir\n", + "files = os.listdir(datastream_dir)\n", + "files[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39b98a36", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: get most recent file\n", + "list_of_files = glob.glob(f\"{datastream_dir}/*\") # * means all if need specific format then *.csv\n", + "latest_file = max(list_of_files, key=os.path.getctime)\n", + "latest_file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "902d514e", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus sort datastream files based on datetime\n", + "files = os.listdir(datastream_dir)\n", + "file_sorted = files.copy()\n", + "file_sorted.sort() \n", + "print(file_sorted[:5])\n", + "\n", + "# to reverse\n", + "file_sorted_reverse = files.copy() \n", + "file_sorted_reverse.sort(reverse=True)\n", + "print(file_sorted_reverse[:5])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec5923b2", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: pattern matching\n", + "# filter the 200709** files under datastream_dir\n", + "files_filter = glob.glob(f'{datastream_dir}/*.200709*.*')\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c4a4aa18", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: use armnotebook_utils.file_filter (TODO) to filter files based on datastream info \n" + ] + }, + { + "cell_type": "markdown", + "id": "cfeb9efc", + "metadata": {}, + "source": [ + "### Load data\n", + "The arm data is stored in [NetCDF](https://en.wikipedia.org/wiki/NetCDF#:~:text=%22NetCDF%20(network%20Common%20Data%20Form,format%20for%20representing%20scientific%20data.) format. We can use xarray's `open_dataset` method to load single file, or `open_mfdataset` to open multiple files. (Note: the latter will still return a single xarray dataset object by combining multiple files.)\n", + "\n", + "Note: open_dataset keeps the file handle open and lazy loads its contents. All parameters are passed directly to open_dataset. It is a preferable method over load_dataset for memory-effiency. (more details, please see [xarray.open_dataset](https://docs.xarray.dev/en/stable/generated/xarray.open_dataset.html))\n", + "\n", + "See the following example in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a440a329", + "metadata": {}, + "outputs": [], + "source": [ + "# open single file\n", + "full_path = latest_file\n", + "print(full_path)\n", + "ds_single = xr.open_dataset(full_path)\n", + "ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0143a3d", + "metadata": {}, + "outputs": [], + "source": [ + "print(ds_single)\n", + "print(type(ds_single))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c0f8939", + "metadata": {}, + "outputs": [], + "source": [ + "# open multiple files\n", + "n_files = 3\n", + "full_paths = [os.path.join(datastream_dir, f_path) for f_path in file_sorted_reverse[:n_files]]\n", + "print(full_paths)\n", + "try: # Note: sometimes multiple files cannot be merged, so we used try except here.\n", + " ds_mutiple = xr.open_mfdataset(full_paths)\n", + " # ds_mutiple\n", + " print(type(ds_mutiple))\n", + " print(ds_mutiple)\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "3fa59943", + "metadata": {}, + "source": [ + "## Explore the data" + ] + }, + { + "cell_type": "markdown", + "id": "571f69b9", + "metadata": {}, + "source": [ + "### NetCDF Data structure \n", + "\n", + "\n", + "(If you are confident with NetCDF basics and xarray essentials, feel free to skip this session.)\n", + "Before we dive into data exploration, there are some eseential concpets we should be familiar with (shown below): \n", + "* Dataset\n", + "* Data array\n", + "* Variable\n", + "* Dimenssion\n", + "* Coordinate\n", + "* Data Type\n", + "* Meta Data (Attributes)\n", + "\n", + "We will not go into details about NetCDF basics and here are some references you might find helpful\n", + "* [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#)\n", + "* [Network Common Data Form (NetCDF)](https://www.unidata.ucar.edu/software/netcdf/) and [A Brief History of (netCDF) Time](https://www.unidata.ucar.edu/software/netcdf/time/recs.html)\n", + "\n", + "\n", + "\n", + "### Xarray essentials\n", + "Earlier we introduced the xarray pacakge and used `open_dataset` and `open_mfdataset` to retrieve NetCDF data file as an xarray Dataset object. Recall that you can review the data in a notebook by using `print(ds)` or `ds`. \n", + "\n", + "Using xarray to retrieve the aforementioned NetCDF basics is straightforwared. In general,\n", + "* Dataset: `ds`\n", + "* Data array: `ds.variables`\n", + "* Variable: `ds.ds.variables`\n", + "* Dimenssion: `ds.dims`\n", + "* Coordinate: `ds.coords`\n", + "* Data Type: `type`\n", + "* Meta Data (Attributes)\n", + "\n", + "Also, here are some references if you are new to xarray\n", + "* [Xarray in 45 minutes](https://tutorial.xarray.dev/overview/xarray-in-45-min.html)\n", + "* [Handling NetCDF Files using XArray for Absolute Beginners](https://towardsdatascience.com/handling-netcdf-files-using-xarray-for-absolute-beginners-111a8ab4463f)\n", + "\n", + "Try the following commands in action\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b3eac323", + "metadata": {}, + "outputs": [], + "source": [ + "ds = ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77ecf85d", + "metadata": {}, + "outputs": [], + "source": [ + "# Dataset \n", + "ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25e7de09", + "metadata": {}, + "outputs": [], + "source": [ + "# Data array, Variable\n", + "# Note: the info can be overwhelming. Do not attempt to grasp everything at the first glance.\n", + "ds.variables" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c41b67e", + "metadata": {}, + "outputs": [], + "source": [ + "# dimenssions\n", + "ds.dims" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "156f1dfc", + "metadata": {}, + "outputs": [], + "source": [ + "# coordinates\n", + "ds.coords" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "277d6064", + "metadata": {}, + "outputs": [], + "source": [ + "# Meta Data (Attributes)\n", + "ds.attrs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d334681f", + "metadata": {}, + "outputs": [], + "source": [ + "# type\n", + "print(type(ds))\n", + "print(type(ds.variables))\n", + "print(type(ds.dims))\n", + "print(type(ds.coords))\n", + "print(type(ds.attrs))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "643399d6", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: retrieve variable names only\n", + "list(ds.variables)" + ] + }, + { + "cell_type": "markdown", + "id": "1cf36878", + "metadata": {}, + "source": [ + "#### Discussion: variable vs. coordinates vs. dimenssions. \n", + "From [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#):\n", + "\n", + "* Dimession: A dimension may be used to represent a real physical dimension, for example, time, latitude, longitude, or height. A dimension might also be used to index other quantities, for example station or model-run-number.\n", + "A netCDF dimension has both a name and a length. A dimension length is an arbitrary positive integer, except that one dimension in a netCDF dataset can have the length UNLIMITED.\n", + "\n", + "* Variables: Variables are used to store the bulk of the data in a netCDF dataset. A variable represents an array of values of the same type. A scalar value is treated as a 0-dimensional array. A variable has a name, a data type, and a shape described by its list of dimensions specified when the variable is created. A variable may also have associated attributes, which may be added, deleted or changed after the variable is created.\n", + "\n", + "* Coordinate (Variables): It is legal for a variable to have the same name as a dimension. Such variables have no special meaning to the netCDF library. However there is a convention that such variables should be treated in a special way by software using this library.\n", + "A variable with the same name as a dimension is called a coordinate variable. It typically defines a physical coordinate corresponding to that dimension.\n", + "\n", + "\n", + "Tips: By definition, coordinates and dimenssions are also variable. For example ds.time is a coordinate but it is also a special variable. But in practice, when we talk about variable, it implies regular/non-coordinate variable. We will use this convention for the remaining notebook. " + ] + }, + { + "cell_type": "markdown", + "id": "d47120f9", + "metadata": {}, + "source": [ + "### Xarray Variable\n", + "\n", + "\n", + "We can use `ds.variables` to access varialbes of a dataset. But many times viewing print-out the whole datasets can be overwhemling. Instead, we would work on the individual variable (also called Xarray data array.) using the `ds[var_name]` syntax, where `var_name` is the name of the variable. See the following examples in action.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5a57e136", + "metadata": {}, + "outputs": [], + "source": [ + "print(type(ds[\"time\"]))\n", + "ds[\"time\"]" + ] + }, + { + "cell_type": "markdown", + "id": "51ec643e", + "metadata": {}, + "source": [ + "#### Variable properties\n", + "For individual varible, the following properties are mostly used with the assocaited xarray method to retrieve them. (assuming var = ds[var_name])\n", + "* name: `var.name`\n", + "* data content: `var.data`\n", + "* attributes: `var.attrs`\n", + "* dimenstions: `var.dims`\n", + "* data type: `var.data.dtype`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a632a525", + "metadata": {}, + "outputs": [], + "source": [ + "var_name = \"time\"\n", + "var = ds[var_name]\n", + "\n", + "print(\"var.name: \\n\", var.name, \"\\n\")\n", + "print(\"var.data: \\n\", var.data, \"\\n\")\n", + "print(\"var.attrs: \\n\", var.attrs, \"\\n\")\n", + "print(\"var.dims: \\n\", var.dims, \"\\n\")\n", + "print(\"var.data.dtype: \\n\", var.data.dtype, \"\\n\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b256b07f", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: indivdual variable info table (collect and display variable info as a tabular format)\n", + "df_info = pd.DataFrame()\n", + "df_info[\"var_name\"] = list(ds.variables)\n", + "df_info[\"dims\"] = df_info.var_name.apply(lambda x: ds[x].dims)\n", + "df_info[\"is_dim\"] = df_info.var_name.apply(lambda x: x in ds.dims)\n", + "df_info[\"n_dim\"] = df_info.var_name.apply(lambda x: len(ds[x].dims))\n", + "df_info[\"attrs\"] = df_info.var_name.apply(lambda x: ds[x].attrs)\n", + "df_info[\"dtype\"] = df_info.var_name.apply(lambda x: ds[x].data.dtype)\n", + "df_info" + ] + }, + { + "cell_type": "markdown", + "id": "e6a36b96", + "metadata": {}, + "source": [ + "### Data cleaning/Preprocessing (skipped)\n", + "Data cleaning and preprocessing is an important stage in the the data analysis pipeline. However it will be out of the scope of this notebook. For more information about data cleaning and preprocessing basics, here are some references. \n", + "* [Xarray Fundamentals](https://earth-env-data-science.github.io/lectures/xarray/xarray.html)\n", + "* [Xarray Tutorial — Pangeo Gallery documentation](http://gallery.pangeo.io/repos/pangeo-data/pangeo-tutorial-gallery/xarray.html)\n", + "* [Pythonic Data Cleaning With pandas and NumPy](https://realpython.com/python-data-cleaning-numpy-pandas/)\n", + "* [Pandas - Cleaning Data](https://www.w3schools.com/python/pandas/pandas_cleaning.asp)" + ] + }, + { + "cell_type": "markdown", + "id": "ae6e1a69", + "metadata": {}, + "source": [ + "### Plotting\n", + "Data visualization (or plotting) is another important data analysis topic and deserves its own discussion. Here in this notebook we will only demonstrate basic tool for simple data visualization tasks.\n", + "\n", + "Here are some reference you might find useful:\n", + "* xarray plotting: https://docs.xarray.dev/en/stable/user-guide/plotting.html\n", + "* Atmospheric Community Toolkit (ACT): https://arm-doe.github.io/ACT/index.html\n", + "\n", + "Note: this notebook is auto-generated using a template. It uses a general idea to select variable(s) to plot and is not customized to each indivdual notebook. Feel free to change the variables in intrrests, especially certain figure is failed to plot." + ] + }, + { + "cell_type": "markdown", + "id": "7e417fb2", + "metadata": {}, + "source": [ + "#### 1-dimenssional basic time series plot\n", + "\n", + "For the following plot we would like to find variables such that\n", + "* it has one and only one dimession\n", + "* \"time\" is its coordinate variable\n", + "* it is not a dimenssion itself,\n", + "* it is not a special variable with substrings within [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "\n", + "Please see the following example in action. (For more details about pandas filtering, please see the following references.)\n", + "* [pandas: multiple conditions while indexing data frame](https://stackoverflow.com/questions/22591174/pandas-multiple-conditions-while-indexing-data-frame-unexpected-behavior)\n", + "* [pandas.DataFrame.apply](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.apply.html)\n", + "* [How to test if a string contains one of the substrings in a list...](https://stackoverflow.com/questions/26577516/how-to-test-if-a-string-contains-one-of-the-substrings-in-a-list-in-pandas" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30edac2d", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==1, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter = df_info[(df_info.n_dim == 1) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4777a659", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_1d = df_filter.var_name.values[0]\n", + " var_1d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb5ae985", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " ds[var_1d].plot()\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "9e41066e", + "metadata": {}, + "source": [ + "#### 2-dimenssional basic plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09bd4adc", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==2, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_2 = df_info[(df_info.n_dim == 2) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2cdea1a9", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_2d = df_filter_2.var_name.values[0]\n", + " var_2d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5f48879", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " print(ds[var_2d].dims)\n", + " # ds[var_2d].plot()\n", + "\n", + " # conventionally, use \"time\" as x-axis\n", + " ds[var_2d].plot(x=\"time\")\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "7554b2a6", + "metadata": {}, + "source": [ + "#### qc-plotting (optional)\n", + "\n", + "Note: act qc plotting has more strict requirement, one of them is the associated qc variable need to have \"flag_masks\" attributes. Which is added by using ds = act.io.armfiles.read_netcdf(files_list), then ds.clean.cleanup()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71c2096f", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " ds_act = act.io.armfiles.read_netcdf(full_path)\n", + " print(type(ds_act))\n", + " ds_act.clean.cleanup()\n", + "\n", + " # or \n", + " # ds.clean.cleanup()\n", + "except Exception as e:\n", + " print(\"ERROR\", e)\n", + " ds_act = ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "844f8505", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter valid variables for ACT qc plotting\n", + "condition = (\"qc_\" + df_info.var_name).apply(lambda x: ds_act[x].attrs.get(\"flag_masks\") is not None \n", + " if x in list(ds.variables) else False)\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_3 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings))) &\n", + " condition\n", + " ]\n", + "df_filter_3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd38cc6", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "try:\n", + " qc_variable = df_filter_3.var_name.values[0]\n", + " print(qc_variable)\n", + "except Exception as e:\n", + " print(e)\n", + " \n", + "try:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + " qc_ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "d76e4d27", + "metadata": {}, + "source": [ + "#### bonus: choose variables to plot from a dropdown menu " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb733804", + "metadata": {}, + "outputs": [], + "source": [ + "# Valid variables filtering\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_4 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82d5c20d", + "metadata": {}, + "outputs": [], + "source": [ + "# example 1: using xarray plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2fc9aaa4", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# fig, ax = plt.subplots(figsize=(10, 4))\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "# fig.clear() # Remove old lines from plot and plot new one\n", + "# if len(ds[var].dims)==2:\n", + "# ds[var].plot(x=\"time\", add_colorbar=False)\n", + "# else:\n", + "# ds[var].plot()\n", + "# plt.grid()\n", + "# plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fabdd802", + "metadata": {}, + "outputs": [], + "source": [ + "# example 2: using act plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "962b4186", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "\n", + "\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "\n", + "# i_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + "# i_display.add_subplots((1,), figsize=(10, 4))\n", + "# ax = i_display.plot(var, subplot_index=(0,), set_title=f\"{var} ({ds_act[var].attrs['long_name']})\",)\n", + "\n", + "# ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "# ax.grid()\n", + "# plt.show()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/COGS/cogs.c1.ipynb b/VAPs/quicklook/COGS/cogs.c1.ipynb new file mode 100644 index 00000000..6534f003 --- /dev/null +++ b/VAPs/quicklook/COGS/cogs.c1.ipynb @@ -0,0 +1,297 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# COGS.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/cogs) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'cogs'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2019-10-27', 'facility': 'N1', 'site': 'sgp', 'start_date': '2017-09-01'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'N1' )\n", + "\n", + "date_start = '2019-10-25'\n", + "date_end = '2019-10-27'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['cldfrac', 'cbh']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'cldfrac'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/DIFFCOR/DIFFCOR_tutorial.ipynb b/VAPs/quicklook/DIFFCOR/DIFFCOR_tutorial.ipynb new file mode 100644 index 00000000..1b872f74 --- /dev/null +++ b/VAPs/quicklook/DIFFCOR/DIFFCOR_tutorial.ipynb @@ -0,0 +1,838 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# BRS1DUTT.C1 Notebook\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/diffcor) for more information about this vap." + ] + }, + { + "cell_type": "markdown", + "id": "97097763", + "metadata": {}, + "source": [ + "In this notebook, we demonstrate the workflow to explore ARM vap data (using brs1dutt as an example.) Value-added products (VAPs) are higher-order data products that have been analyzed and processed to ease scientists’ use of ARM data in atmospheric research and global climate models. \n", + "Here is the main content we will cover." + ] + }, + { + "cell_type": "markdown", + "id": "eddec40f", + "metadata": {}, + "source": [ + "# Table of Content\n", + "## Access the data\n", + "* How to retrieve the data\n", + "* Data path and file name conventions\n", + "* Load data\n", + "## Explore the data\n", + "* NetCDF Data structure\n", + "* Xarray essentials\n", + "* Xarray Variable\n", + "## Plot the data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "import random\n", + "\n", + "import glob\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "2372d5be", + "metadata": {}, + "source": [ + "## Access the data" + ] + }, + { + "cell_type": "markdown", + "id": "5b0d9684", + "metadata": {}, + "source": [ + "### How to retrieve the data\n", + "We assume the path \"/data/archive\" is available where you are running this notebook. You can use `os.path.exists(\"/data/archive\")` to verify if the path exists at your machine.\n", + "\n", + "\n", + "### Data path and file name conventions\n", + "There several common terminologies regarding ARM data, for example, data-stream-name, data-level, site, facility, instrument, etc. (For more details, please see [ARM Data File Standards Version 1.2](https://www.google.com/search?q=arm+datastream+facility+cite+definition&rlz=1C1GCEJ_enUS1029US1029&ei=hb41ZICrBPukqtsPvMODIA&ved=0ahUKEwjAgeiS0aL-AhV7kmoFHbzhAAQQ4dUDCBA&uact=5&oq=arm+datastream+facility+cite+definition&gs_lcp=Cgxnd3Mtd2l6LXNlcnAQAzIFCAAQogQyBQgAEKIEMgUIABCiBDIFCAAQogQyBQgAEKIEOgoIABBHENYEELADOgoIIRCgARDDBBAKSgQIQRgAUJ4DWIoOYO0PaAFwAXgAgAGMAYgBuQmSAQMyLjmYAQCgAQHIAQjAAQE&sclient=gws-wiz-serp).) \n", + "\n", + "For example, this notebook is called `brs1dutt.c1`, where `brs1dutt` is the \"datastream name\", and the `{process.ds_class_level}}` is the \"data level\".\n", + "\n", + "This datastream also contains site `sgp` and facility `C1`. (Note: individual datastream might have multiple site-facility pairs.)\n", + "In such a case, the data of this data-stream is stored at `/data/archive/sgp/sgpbrs1duttC1.c1`, which is in the format of `//.`. We can use the following method to assign the data-stream directory `datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )`\n", + "\n", + "The data files under datastream_dir also follows naming conventions. But once reach the datastream_dir level, the most import file naming convention to differentiate the files is \"yyyyMMdd.hhmmss\", which comes handy to filter out files based on datetime. For example, we can use `glob.glob(f'{datastream_dir}/*.200709*.*')` to filter files in 2007 September.\n", + "\n", + "(Note: refer to the https://adc.arm.gov/discovery/#/ and https://adc.arm.gov/solr8/metadata/select API to explore ARM datastream and assoicated available sites and facilities\n", + "\n", + "Please see the following examples in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d7e9eb85", + "metadata": {}, + "outputs": [], + "source": [ + "# Verify if DATA_DIR path exists\n", + "DATA_DIR = \"/data/archive\"\n", + "os.path.exists(DATA_DIR)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "586993fd", + "metadata": {}, + "outputs": [], + "source": [ + "# Speicify datastream_dir following the path conventions and check its existence\n", + "DATASTREAM_NAME = \"brs1dutt\"\n", + "DATA_LEVEL = \"c1\"\n", + "site = \"sgp\"\n", + "facility = \"C1\"\n", + "datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )\n", + "print(datastream_dir)\n", + "print(os.path.exists(datastream_dir))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0742f7c1", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: list 5 (random) files under datastream_dir\n", + "files = os.listdir(datastream_dir)\n", + "files[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39b98a36", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: get most recent file\n", + "list_of_files = glob.glob(f\"{datastream_dir}/*\") # * means all if need specific format then *.csv\n", + "latest_file = max(list_of_files, key=os.path.getctime)\n", + "latest_file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "902d514e", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus sort datastream files based on datetime\n", + "files = os.listdir(datastream_dir)\n", + "file_sorted = files.copy()\n", + "file_sorted.sort() \n", + "print(file_sorted[:5])\n", + "\n", + "# to reverse\n", + "file_sorted_reverse = files.copy() \n", + "file_sorted_reverse.sort(reverse=True)\n", + "print(file_sorted_reverse[:5])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec5923b2", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: pattern matching\n", + "# filter the 200709** files under datastream_dir\n", + "files_filter = glob.glob(f'{datastream_dir}/*.200709*.*')\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c4a4aa18", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: use armnotebook_utils.file_filter (TODO) to filter files based on datastream info \n" + ] + }, + { + "cell_type": "markdown", + "id": "cfeb9efc", + "metadata": {}, + "source": [ + "### Load data\n", + "The arm data is stored in [NetCDF](https://en.wikipedia.org/wiki/NetCDF#:~:text=%22NetCDF%20(network%20Common%20Data%20Form,format%20for%20representing%20scientific%20data.) format. We can use xarray's `open_dataset` method to load single file, or `open_mfdataset` to open multiple files. (Note: the latter will still return a single xarray dataset object by combining multiple files.)\n", + "\n", + "Note: open_dataset keeps the file handle open and lazy loads its contents. All parameters are passed directly to open_dataset. It is a preferable method over load_dataset for memory-effiency. (more details, please see [xarray.open_dataset](https://docs.xarray.dev/en/stable/generated/xarray.open_dataset.html))\n", + "\n", + "See the following example in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a440a329", + "metadata": {}, + "outputs": [], + "source": [ + "# open single file\n", + "full_path = latest_file\n", + "print(full_path)\n", + "ds_single = xr.open_dataset(full_path)\n", + "ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0143a3d", + "metadata": {}, + "outputs": [], + "source": [ + "print(ds_single)\n", + "print(type(ds_single))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c0f8939", + "metadata": {}, + "outputs": [], + "source": [ + "# open multiple files\n", + "n_files = 3\n", + "full_paths = [os.path.join(datastream_dir, f_path) for f_path in file_sorted_reverse[:n_files]]\n", + "print(full_paths)\n", + "try: # Note: sometimes multiple files cannot be merged, so we used try except here.\n", + " ds_mutiple = xr.open_mfdataset(full_paths)\n", + " # ds_mutiple\n", + " print(type(ds_mutiple))\n", + " print(ds_mutiple)\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "3fa59943", + "metadata": {}, + "source": [ + "## Explore the data" + ] + }, + { + "cell_type": "markdown", + "id": "571f69b9", + "metadata": {}, + "source": [ + "### NetCDF Data structure \n", + "\n", + "\n", + "(If you are confident with NetCDF basics and xarray essentials, feel free to skip this session.)\n", + "Before we dive into data exploration, there are some eseential concpets we should be familiar with (shown below): \n", + "* Dataset\n", + "* Data array\n", + "* Variable\n", + "* Dimenssion\n", + "* Coordinate\n", + "* Data Type\n", + "* Meta Data (Attributes)\n", + "\n", + "We will not go into details about NetCDF basics and here are some references you might find helpful\n", + "* [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#)\n", + "* [Network Common Data Form (NetCDF)](https://www.unidata.ucar.edu/software/netcdf/) and [A Brief History of (netCDF) Time](https://www.unidata.ucar.edu/software/netcdf/time/recs.html)\n", + "\n", + "\n", + "\n", + "### Xarray essentials\n", + "Earlier we introduced the xarray pacakge and used `open_dataset` and `open_mfdataset` to retrieve NetCDF data file as an xarray Dataset object. Recall that you can review the data in a notebook by using `print(ds)` or `ds`. \n", + "\n", + "Using xarray to retrieve the aforementioned NetCDF basics is straightforwared. In general,\n", + "* Dataset: `ds`\n", + "* Data array: `ds.variables`\n", + "* Variable: `ds.ds.variables`\n", + "* Dimenssion: `ds.dims`\n", + "* Coordinate: `ds.coords`\n", + "* Data Type: `type`\n", + "* Meta Data (Attributes)\n", + "\n", + "Also, here are some references if you are new to xarray\n", + "* [Xarray in 45 minutes](https://tutorial.xarray.dev/overview/xarray-in-45-min.html)\n", + "* [Handling NetCDF Files using XArray for Absolute Beginners](https://towardsdatascience.com/handling-netcdf-files-using-xarray-for-absolute-beginners-111a8ab4463f)\n", + "\n", + "Try the following commands in action\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b3eac323", + "metadata": {}, + "outputs": [], + "source": [ + "ds = ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77ecf85d", + "metadata": {}, + "outputs": [], + "source": [ + "# Dataset \n", + "ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25e7de09", + "metadata": {}, + "outputs": [], + "source": [ + "# Data array, Variable\n", + "# Note: the info can be overwhelming. Do not attempt to grasp everything at the first glance.\n", + "ds.variables" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c41b67e", + "metadata": {}, + "outputs": [], + "source": [ + "# dimenssions\n", + "ds.dims" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "156f1dfc", + "metadata": {}, + "outputs": [], + "source": [ + "# coordinates\n", + "ds.coords" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "277d6064", + "metadata": {}, + "outputs": [], + "source": [ + "# Meta Data (Attributes)\n", + "ds.attrs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d334681f", + "metadata": {}, + "outputs": [], + "source": [ + "# type\n", + "print(type(ds))\n", + "print(type(ds.variables))\n", + "print(type(ds.dims))\n", + "print(type(ds.coords))\n", + "print(type(ds.attrs))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "643399d6", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: retrieve variable names only\n", + "list(ds.variables)" + ] + }, + { + "cell_type": "markdown", + "id": "1cf36878", + "metadata": {}, + "source": [ + "#### Discussion: variable vs. coordinates vs. dimenssions. \n", + "From [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#):\n", + "\n", + "* Dimession: A dimension may be used to represent a real physical dimension, for example, time, latitude, longitude, or height. A dimension might also be used to index other quantities, for example station or model-run-number.\n", + "A netCDF dimension has both a name and a length. A dimension length is an arbitrary positive integer, except that one dimension in a netCDF dataset can have the length UNLIMITED.\n", + "\n", + "* Variables: Variables are used to store the bulk of the data in a netCDF dataset. A variable represents an array of values of the same type. A scalar value is treated as a 0-dimensional array. A variable has a name, a data type, and a shape described by its list of dimensions specified when the variable is created. A variable may also have associated attributes, which may be added, deleted or changed after the variable is created.\n", + "\n", + "* Coordinate (Variables): It is legal for a variable to have the same name as a dimension. Such variables have no special meaning to the netCDF library. However there is a convention that such variables should be treated in a special way by software using this library.\n", + "A variable with the same name as a dimension is called a coordinate variable. It typically defines a physical coordinate corresponding to that dimension.\n", + "\n", + "\n", + "Tips: By definition, coordinates and dimenssions are also variable. For example ds.time is a coordinate but it is also a special variable. But in practice, when we talk about variable, it implies regular/non-coordinate variable. We will use this convention for the remaining notebook. " + ] + }, + { + "cell_type": "markdown", + "id": "d47120f9", + "metadata": {}, + "source": [ + "### Xarray Variable\n", + "\n", + "\n", + "We can use `ds.variables` to access varialbes of a dataset. But many times viewing print-out the whole datasets can be overwhemling. Instead, we would work on the individual variable (also called Xarray data array.) using the `ds[var_name]` syntax, where `var_name` is the name of the variable. See the following examples in action.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5a57e136", + "metadata": {}, + "outputs": [], + "source": [ + "print(type(ds[\"time\"]))\n", + "ds[\"time\"]" + ] + }, + { + "cell_type": "markdown", + "id": "51ec643e", + "metadata": {}, + "source": [ + "#### Variable properties\n", + "For individual varible, the following properties are mostly used with the assocaited xarray method to retrieve them. (assuming var = ds[var_name])\n", + "* name: `var.name`\n", + "* data content: `var.data`\n", + "* attributes: `var.attrs`\n", + "* dimenstions: `var.dims`\n", + "* data type: `var.data.dtype`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a632a525", + "metadata": {}, + "outputs": [], + "source": [ + "var_name = \"time\"\n", + "var = ds[var_name]\n", + "\n", + "print(\"var.name: \\n\", var.name, \"\\n\")\n", + "print(\"var.data: \\n\", var.data, \"\\n\")\n", + "print(\"var.attrs: \\n\", var.attrs, \"\\n\")\n", + "print(\"var.dims: \\n\", var.dims, \"\\n\")\n", + "print(\"var.data.dtype: \\n\", var.data.dtype, \"\\n\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b256b07f", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: indivdual variable info table (collect and display variable info as a tabular format)\n", + "df_info = pd.DataFrame()\n", + "df_info[\"var_name\"] = list(ds.variables)\n", + "df_info[\"dims\"] = df_info.var_name.apply(lambda x: ds[x].dims)\n", + "df_info[\"is_dim\"] = df_info.var_name.apply(lambda x: x in ds.dims)\n", + "df_info[\"n_dim\"] = df_info.var_name.apply(lambda x: len(ds[x].dims))\n", + "df_info[\"attrs\"] = df_info.var_name.apply(lambda x: ds[x].attrs)\n", + "df_info[\"dtype\"] = df_info.var_name.apply(lambda x: ds[x].data.dtype)\n", + "df_info" + ] + }, + { + "cell_type": "markdown", + "id": "e6a36b96", + "metadata": {}, + "source": [ + "### Data cleaning/Preprocessing (skipped)\n", + "Data cleaning and preprocessing is an important stage in the the data analysis pipeline. However it will be out of the scope of this notebook. For more information about data cleaning and preprocessing basics, here are some references. \n", + "* [Xarray Fundamentals](https://earth-env-data-science.github.io/lectures/xarray/xarray.html)\n", + "* [Xarray Tutorial — Pangeo Gallery documentation](http://gallery.pangeo.io/repos/pangeo-data/pangeo-tutorial-gallery/xarray.html)\n", + "* [Pythonic Data Cleaning With pandas and NumPy](https://realpython.com/python-data-cleaning-numpy-pandas/)\n", + "* [Pandas - Cleaning Data](https://www.w3schools.com/python/pandas/pandas_cleaning.asp)" + ] + }, + { + "cell_type": "markdown", + "id": "ae6e1a69", + "metadata": {}, + "source": [ + "### Plotting\n", + "Data visualization (or plotting) is another important data analysis topic and deserves its own discussion. Here in this notebook we will only demonstrate basic tool for simple data visualization tasks.\n", + "\n", + "Here are some reference you might find useful:\n", + "* xarray plotting: https://docs.xarray.dev/en/stable/user-guide/plotting.html\n", + "* Atmospheric Community Toolkit (ACT): https://arm-doe.github.io/ACT/index.html\n", + "\n", + "Note: this notebook is auto-generated using a template. It uses a general idea to select variable(s) to plot and is not customized to each indivdual notebook. Feel free to change the variables in intrrests, especially certain figure is failed to plot." + ] + }, + { + "cell_type": "markdown", + "id": "7e417fb2", + "metadata": {}, + "source": [ + "#### 1-dimenssional basic time series plot\n", + "\n", + "For the following plot we would like to find variables such that\n", + "* it has one and only one dimession\n", + "* \"time\" is its coordinate variable\n", + "* it is not a dimenssion itself,\n", + "* it is not a special variable with substrings within [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "\n", + "Please see the following example in action. (For more details about pandas filtering, please see the following references.)\n", + "* [pandas: multiple conditions while indexing data frame](https://stackoverflow.com/questions/22591174/pandas-multiple-conditions-while-indexing-data-frame-unexpected-behavior)\n", + "* [pandas.DataFrame.apply](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.apply.html)\n", + "* [How to test if a string contains one of the substrings in a list...](https://stackoverflow.com/questions/26577516/how-to-test-if-a-string-contains-one-of-the-substrings-in-a-list-in-pandas" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30edac2d", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==1, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter = df_info[(df_info.n_dim == 1) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4777a659", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_1d = df_filter.var_name.values[0]\n", + " var_1d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb5ae985", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " ds[var_1d].plot()\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "9e41066e", + "metadata": {}, + "source": [ + "#### 2-dimenssional basic plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09bd4adc", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==2, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_2 = df_info[(df_info.n_dim == 2) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2cdea1a9", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_2d = df_filter_2.var_name.values[0]\n", + " var_2d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5f48879", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " print(ds[var_2d].dims)\n", + " # ds[var_2d].plot()\n", + "\n", + " # conventionally, use \"time\" as x-axis\n", + " ds[var_2d].plot(x=\"time\")\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "7554b2a6", + "metadata": {}, + "source": [ + "#### qc-plotting (optional)\n", + "\n", + "Note: act qc plotting has more strict requirement, one of them is the associated qc variable need to have \"flag_masks\" attributes. Which is added by using ds = act.io.armfiles.read_netcdf(files_list), then ds.clean.cleanup()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71c2096f", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " ds_act = act.io.armfiles.read_netcdf(full_path)\n", + " print(type(ds_act))\n", + " ds_act.clean.cleanup()\n", + "\n", + " # or \n", + " # ds.clean.cleanup()\n", + "except Exception as e:\n", + " print(\"ERROR\", e)\n", + " ds_act = ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "844f8505", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter valid variables for ACT qc plotting\n", + "condition = (\"qc_\" + df_info.var_name).apply(lambda x: ds_act[x].attrs.get(\"flag_masks\") is not None \n", + " if x in list(ds.variables) else False)\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_3 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings))) &\n", + " condition\n", + " ]\n", + "df_filter_3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd38cc6", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "try:\n", + " qc_variable = df_filter_3.var_name.values[0]\n", + " print(qc_variable)\n", + "except Exception as e:\n", + " print(e)\n", + " \n", + "try:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + " qc_ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "d76e4d27", + "metadata": {}, + "source": [ + "#### bonus: choose variables to plot from a dropdown menu " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb733804", + "metadata": {}, + "outputs": [], + "source": [ + "# Valid variables filtering\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_4 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82d5c20d", + "metadata": {}, + "outputs": [], + "source": [ + "# example 1: using xarray plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2fc9aaa4", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# fig, ax = plt.subplots(figsize=(10, 4))\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "# fig.clear() # Remove old lines from plot and plot new one\n", + "# if len(ds[var].dims)==2:\n", + "# ds[var].plot(x=\"time\", add_colorbar=False)\n", + "# else:\n", + "# ds[var].plot()\n", + "# plt.grid()\n", + "# plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fabdd802", + "metadata": {}, + "outputs": [], + "source": [ + "# example 2: using act plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "962b4186", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "\n", + "\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "\n", + "# i_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + "# i_display.add_subplots((1,), figsize=(10, 4))\n", + "# ax = i_display.plot(var, subplot_index=(0,), set_title=f\"{var} ({ds_act[var].attrs['long_name']})\",)\n", + "\n", + "# ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "# ax.grid()\n", + "# plt.show()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/DIFFCOR/brs1dutt.c1.ipynb b/VAPs/quicklook/DIFFCOR/brs1dutt.c1.ipynb new file mode 100644 index 00000000..d33149cf --- /dev/null +++ b/VAPs/quicklook/DIFFCOR/brs1dutt.c1.ipynb @@ -0,0 +1,297 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# BRS1DUTT.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/diffcor) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'brs1dutt'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2001-01-22', 'facility': 'C1', 'site': 'sgp', 'start_date': '1993-09-01'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2001-01-20'\n", + "date_end = '2001-01-22'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['down_short_hemisp_sum', 'status_down_short_hemisp_sum', 'up_short_hemisp']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'down_short_hemisp_sum'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/DIFFCOR/siros1dutt.c1.ipynb b/VAPs/quicklook/DIFFCOR/siros1dutt.c1.ipynb new file mode 100644 index 00000000..f5e4513d --- /dev/null +++ b/VAPs/quicklook/DIFFCOR/siros1dutt.c1.ipynb @@ -0,0 +1,297 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# SIROS1DUTT.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/diffcor) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'siros1dutt'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '1997-10-30', 'facility': 'E10', 'site': 'sgp', 'start_date': '1995-07-21'}, {'end_date': '1997-08-21', 'facility': 'E11', 'site': 'sgp', 'start_date': '1995-06-30'}, {'end_date': '1997-10-26', 'facility': 'E12', 'site': 'sgp', 'start_date': '1996-01-19'}, {'end_date': '1997-08-25', 'facility': 'E13', 'site': 'sgp', 'start_date': '1994-01-07'}, {'end_date': '1997-08-14', 'facility': 'E15', 'site': 'sgp', 'start_date': '1994-01-12'}, {'end_date': '1997-08-20', 'facility': 'E16', 'site': 'sgp', 'start_date': '1995-06-02'}, {'end_date': '1997-09-15', 'facility': 'E18', 'site': 'sgp', 'start_date': '1996-06-20'}, {'end_date': '1997-11-20', 'facility': 'E1', 'site': 'sgp', 'start_date': '1995-11-15'}, {'end_date': '1998-02-10', 'facility': 'E20', 'site': 'sgp', 'start_date': '1994-11-03'}, {'end_date': '1997-11-25', 'facility': 'E22', 'site': 'sgp', 'start_date': '1995-03-16'}, {'end_date': '1997-11-25', 'facility': 'E24', 'site': 'sgp', 'start_date': '1995-11-07'}, {'end_date': '1997-11-05', 'facility': 'E2', 'site': 'sgp', 'start_date': '1996-03-07'}, {'end_date': '1997-11-04', 'facility': 'E3', 'site': 'sgp', 'start_date': '1996-03-06'}, {'end_date': '1997-08-07', 'facility': 'E4', 'site': 'sgp', 'start_date': '1995-05-08'}, {'end_date': '1997-11-06', 'facility': 'E5', 'site': 'sgp', 'start_date': '1996-06-14'}, {'end_date': '1997-11-05', 'facility': 'E6', 'site': 'sgp', 'start_date': '1996-03-05'}, {'end_date': '1997-10-31', 'facility': 'E7', 'site': 'sgp', 'start_date': '1995-05-18'}, {'end_date': '1997-07-14', 'facility': 'E8', 'site': 'sgp', 'start_date': '1995-09-22'}, {'end_date': '1998-02-03', 'facility': 'E9', 'site': 'sgp', 'start_date': '1994-01-12'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'E10' )\n", + "\n", + "date_start = '1997-10-28'\n", + "date_end = '1997-10-30'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['down_short_hemisp_sum', 'status_down_short_hemisp_sum', 'up_short_hemisp']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'down_short_hemisp_sum'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/DIFFCOR/sirs1dutt.c1.ipynb b/VAPs/quicklook/DIFFCOR/sirs1dutt.c1.ipynb new file mode 100644 index 00000000..faca610d --- /dev/null +++ b/VAPs/quicklook/DIFFCOR/sirs1dutt.c1.ipynb @@ -0,0 +1,297 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# SIRS1DUTT.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/diffcor) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'sirs1dutt'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '1999-04-13', 'facility': 'C1', 'site': 'sgp', 'start_date': '1997-03-21'}, {'end_date': '2001-02-14', 'facility': 'E10', 'site': 'sgp', 'start_date': '1997-11-05'}, {'end_date': '2001-02-20', 'facility': 'E11', 'site': 'sgp', 'start_date': '1997-08-22'}, {'end_date': '2001-02-20', 'facility': 'E12', 'site': 'sgp', 'start_date': '1997-10-31'}, {'end_date': '2001-02-22', 'facility': 'E13', 'site': 'sgp', 'start_date': '1997-08-29'}, {'end_date': '2001-02-20', 'facility': 'E15', 'site': 'sgp', 'start_date': '1997-08-26'}, {'end_date': '2001-02-21', 'facility': 'E16', 'site': 'sgp', 'start_date': '1997-08-21'}, {'end_date': '2001-02-20', 'facility': 'E18', 'site': 'sgp', 'start_date': '1997-10-15'}, {'end_date': '2001-02-22', 'facility': 'E19', 'site': 'sgp', 'start_date': '1998-07-08'}, {'end_date': '2001-02-12', 'facility': 'E1', 'site': 'sgp', 'start_date': '1997-11-21'}, {'end_date': '2001-02-21', 'facility': 'E20', 'site': 'sgp', 'start_date': '1998-02-13'}, {'end_date': '2001-02-20', 'facility': 'E21', 'site': 'sgp', 'start_date': '1999-09-11'}, {'end_date': '2001-02-21', 'facility': 'E22', 'site': 'sgp', 'start_date': '1997-11-25'}, {'end_date': '2001-02-22', 'facility': 'E24', 'site': 'sgp', 'start_date': '1997-12-03'}, {'end_date': '2001-02-21', 'facility': 'E25', 'site': 'sgp', 'start_date': '1997-11-12'}, {'end_date': '2001-02-14', 'facility': 'E2', 'site': 'sgp', 'start_date': '1997-11-06'}, {'end_date': '2001-02-14', 'facility': 'E3', 'site': 'sgp', 'start_date': '1997-11-05'}, {'end_date': '2001-02-14', 'facility': 'E4', 'site': 'sgp', 'start_date': '1997-11-10'}, {'end_date': '2001-02-14', 'facility': 'E5', 'site': 'sgp', 'start_date': '1997-12-18'}, {'end_date': '2001-02-15', 'facility': 'E6', 'site': 'sgp', 'start_date': '1997-11-06'}, {'end_date': '2001-02-13', 'facility': 'E7', 'site': 'sgp', 'start_date': '1997-10-31'}, {'end_date': '2001-02-13', 'facility': 'E8', 'site': 'sgp', 'start_date': '1997-08-21'}, {'end_date': '2001-02-13', 'facility': 'E9', 'site': 'sgp', 'start_date': '1998-02-07'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '1999-04-11'\n", + "date_end = '1999-04-13'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['down_short_hemisp_sum', 'status_down_short_hemisp_sum', 'up_short_hemisp']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'down_short_hemisp_sum'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/DLPROF-WIND/DLPROF-WIND_tutorial.ipynb b/VAPs/quicklook/DLPROF-WIND/DLPROF-WIND_tutorial.ipynb new file mode 100644 index 00000000..7e46950f --- /dev/null +++ b/VAPs/quicklook/DLPROF-WIND/DLPROF-WIND_tutorial.ipynb @@ -0,0 +1,838 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# DLPROFWIND4NEWS.C1 Notebook\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/dlprof-wind) for more information about this vap." + ] + }, + { + "cell_type": "markdown", + "id": "97097763", + "metadata": {}, + "source": [ + "In this notebook, we demonstrate the workflow to explore ARM vap data (using dlprofwind4news as an example.) Value-added products (VAPs) are higher-order data products that have been analyzed and processed to ease scientists’ use of ARM data in atmospheric research and global climate models. \n", + "Here is the main content we will cover." + ] + }, + { + "cell_type": "markdown", + "id": "eddec40f", + "metadata": {}, + "source": [ + "# Table of Content\n", + "## Access the data\n", + "* How to retrieve the data\n", + "* Data path and file name conventions\n", + "* Load data\n", + "## Explore the data\n", + "* NetCDF Data structure\n", + "* Xarray essentials\n", + "* Xarray Variable\n", + "## Plot the data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "import random\n", + "\n", + "import glob\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "2372d5be", + "metadata": {}, + "source": [ + "## Access the data" + ] + }, + { + "cell_type": "markdown", + "id": "5b0d9684", + "metadata": {}, + "source": [ + "### How to retrieve the data\n", + "We assume the path \"/data/archive\" is available where you are running this notebook. You can use `os.path.exists(\"/data/archive\")` to verify if the path exists at your machine.\n", + "\n", + "\n", + "### Data path and file name conventions\n", + "There several common terminologies regarding ARM data, for example, data-stream-name, data-level, site, facility, instrument, etc. (For more details, please see [ARM Data File Standards Version 1.2](https://www.google.com/search?q=arm+datastream+facility+cite+definition&rlz=1C1GCEJ_enUS1029US1029&ei=hb41ZICrBPukqtsPvMODIA&ved=0ahUKEwjAgeiS0aL-AhV7kmoFHbzhAAQQ4dUDCBA&uact=5&oq=arm+datastream+facility+cite+definition&gs_lcp=Cgxnd3Mtd2l6LXNlcnAQAzIFCAAQogQyBQgAEKIEMgUIABCiBDIFCAAQogQyBQgAEKIEOgoIABBHENYEELADOgoIIRCgARDDBBAKSgQIQRgAUJ4DWIoOYO0PaAFwAXgAgAGMAYgBuQmSAQMyLjmYAQCgAQHIAQjAAQE&sclient=gws-wiz-serp).) \n", + "\n", + "For example, this notebook is called `dlprofwind4news.c1`, where `dlprofwind4news` is the \"datastream name\", and the `{process.ds_class_level}}` is the \"data level\".\n", + "\n", + "This datastream also contains site `asi` and facility `M1`. (Note: individual datastream might have multiple site-facility pairs.)\n", + "In such a case, the data of this data-stream is stored at `/data/archive/asi/asidlprofwind4newsM1.c1`, which is in the format of `//.`. We can use the following method to assign the data-stream directory `datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )`\n", + "\n", + "The data files under datastream_dir also follows naming conventions. But once reach the datastream_dir level, the most import file naming convention to differentiate the files is \"yyyyMMdd.hhmmss\", which comes handy to filter out files based on datetime. For example, we can use `glob.glob(f'{datastream_dir}/*.200709*.*')` to filter files in 2007 September.\n", + "\n", + "(Note: refer to the https://adc.arm.gov/discovery/#/ and https://adc.arm.gov/solr8/metadata/select API to explore ARM datastream and assoicated available sites and facilities\n", + "\n", + "Please see the following examples in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d7e9eb85", + "metadata": {}, + "outputs": [], + "source": [ + "# Verify if DATA_DIR path exists\n", + "DATA_DIR = \"/data/archive\"\n", + "os.path.exists(DATA_DIR)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "586993fd", + "metadata": {}, + "outputs": [], + "source": [ + "# Speicify datastream_dir following the path conventions and check its existence\n", + "DATASTREAM_NAME = \"dlprofwind4news\"\n", + "DATA_LEVEL = \"c1\"\n", + "site = \"asi\"\n", + "facility = \"M1\"\n", + "datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )\n", + "print(datastream_dir)\n", + "print(os.path.exists(datastream_dir))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0742f7c1", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: list 5 (random) files under datastream_dir\n", + "files = os.listdir(datastream_dir)\n", + "files[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39b98a36", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: get most recent file\n", + "list_of_files = glob.glob(f\"{datastream_dir}/*\") # * means all if need specific format then *.csv\n", + "latest_file = max(list_of_files, key=os.path.getctime)\n", + "latest_file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "902d514e", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus sort datastream files based on datetime\n", + "files = os.listdir(datastream_dir)\n", + "file_sorted = files.copy()\n", + "file_sorted.sort() \n", + "print(file_sorted[:5])\n", + "\n", + "# to reverse\n", + "file_sorted_reverse = files.copy() \n", + "file_sorted_reverse.sort(reverse=True)\n", + "print(file_sorted_reverse[:5])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec5923b2", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: pattern matching\n", + "# filter the 200709** files under datastream_dir\n", + "files_filter = glob.glob(f'{datastream_dir}/*.200709*.*')\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c4a4aa18", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: use armnotebook_utils.file_filter (TODO) to filter files based on datastream info \n" + ] + }, + { + "cell_type": "markdown", + "id": "cfeb9efc", + "metadata": {}, + "source": [ + "### Load data\n", + "The arm data is stored in [NetCDF](https://en.wikipedia.org/wiki/NetCDF#:~:text=%22NetCDF%20(network%20Common%20Data%20Form,format%20for%20representing%20scientific%20data.) format. We can use xarray's `open_dataset` method to load single file, or `open_mfdataset` to open multiple files. (Note: the latter will still return a single xarray dataset object by combining multiple files.)\n", + "\n", + "Note: open_dataset keeps the file handle open and lazy loads its contents. All parameters are passed directly to open_dataset. It is a preferable method over load_dataset for memory-effiency. (more details, please see [xarray.open_dataset](https://docs.xarray.dev/en/stable/generated/xarray.open_dataset.html))\n", + "\n", + "See the following example in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a440a329", + "metadata": {}, + "outputs": [], + "source": [ + "# open single file\n", + "full_path = latest_file\n", + "print(full_path)\n", + "ds_single = xr.open_dataset(full_path)\n", + "ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0143a3d", + "metadata": {}, + "outputs": [], + "source": [ + "print(ds_single)\n", + "print(type(ds_single))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c0f8939", + "metadata": {}, + "outputs": [], + "source": [ + "# open multiple files\n", + "n_files = 3\n", + "full_paths = [os.path.join(datastream_dir, f_path) for f_path in file_sorted_reverse[:n_files]]\n", + "print(full_paths)\n", + "try: # Note: sometimes multiple files cannot be merged, so we used try except here.\n", + " ds_mutiple = xr.open_mfdataset(full_paths)\n", + " # ds_mutiple\n", + " print(type(ds_mutiple))\n", + " print(ds_mutiple)\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "3fa59943", + "metadata": {}, + "source": [ + "## Explore the data" + ] + }, + { + "cell_type": "markdown", + "id": "571f69b9", + "metadata": {}, + "source": [ + "### NetCDF Data structure \n", + "\n", + "\n", + "(If you are confident with NetCDF basics and xarray essentials, feel free to skip this session.)\n", + "Before we dive into data exploration, there are some eseential concpets we should be familiar with (shown below): \n", + "* Dataset\n", + "* Data array\n", + "* Variable\n", + "* Dimenssion\n", + "* Coordinate\n", + "* Data Type\n", + "* Meta Data (Attributes)\n", + "\n", + "We will not go into details about NetCDF basics and here are some references you might find helpful\n", + "* [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#)\n", + "* [Network Common Data Form (NetCDF)](https://www.unidata.ucar.edu/software/netcdf/) and [A Brief History of (netCDF) Time](https://www.unidata.ucar.edu/software/netcdf/time/recs.html)\n", + "\n", + "\n", + "\n", + "### Xarray essentials\n", + "Earlier we introduced the xarray pacakge and used `open_dataset` and `open_mfdataset` to retrieve NetCDF data file as an xarray Dataset object. Recall that you can review the data in a notebook by using `print(ds)` or `ds`. \n", + "\n", + "Using xarray to retrieve the aforementioned NetCDF basics is straightforwared. In general,\n", + "* Dataset: `ds`\n", + "* Data array: `ds.variables`\n", + "* Variable: `ds.ds.variables`\n", + "* Dimenssion: `ds.dims`\n", + "* Coordinate: `ds.coords`\n", + "* Data Type: `type`\n", + "* Meta Data (Attributes)\n", + "\n", + "Also, here are some references if you are new to xarray\n", + "* [Xarray in 45 minutes](https://tutorial.xarray.dev/overview/xarray-in-45-min.html)\n", + "* [Handling NetCDF Files using XArray for Absolute Beginners](https://towardsdatascience.com/handling-netcdf-files-using-xarray-for-absolute-beginners-111a8ab4463f)\n", + "\n", + "Try the following commands in action\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b3eac323", + "metadata": {}, + "outputs": [], + "source": [ + "ds = ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77ecf85d", + "metadata": {}, + "outputs": [], + "source": [ + "# Dataset \n", + "ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25e7de09", + "metadata": {}, + "outputs": [], + "source": [ + "# Data array, Variable\n", + "# Note: the info can be overwhelming. Do not attempt to grasp everything at the first glance.\n", + "ds.variables" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c41b67e", + "metadata": {}, + "outputs": [], + "source": [ + "# dimenssions\n", + "ds.dims" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "156f1dfc", + "metadata": {}, + "outputs": [], + "source": [ + "# coordinates\n", + "ds.coords" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "277d6064", + "metadata": {}, + "outputs": [], + "source": [ + "# Meta Data (Attributes)\n", + "ds.attrs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d334681f", + "metadata": {}, + "outputs": [], + "source": [ + "# type\n", + "print(type(ds))\n", + "print(type(ds.variables))\n", + "print(type(ds.dims))\n", + "print(type(ds.coords))\n", + "print(type(ds.attrs))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "643399d6", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: retrieve variable names only\n", + "list(ds.variables)" + ] + }, + { + "cell_type": "markdown", + "id": "1cf36878", + "metadata": {}, + "source": [ + "#### Discussion: variable vs. coordinates vs. dimenssions. \n", + "From [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#):\n", + "\n", + "* Dimession: A dimension may be used to represent a real physical dimension, for example, time, latitude, longitude, or height. A dimension might also be used to index other quantities, for example station or model-run-number.\n", + "A netCDF dimension has both a name and a length. A dimension length is an arbitrary positive integer, except that one dimension in a netCDF dataset can have the length UNLIMITED.\n", + "\n", + "* Variables: Variables are used to store the bulk of the data in a netCDF dataset. A variable represents an array of values of the same type. A scalar value is treated as a 0-dimensional array. A variable has a name, a data type, and a shape described by its list of dimensions specified when the variable is created. A variable may also have associated attributes, which may be added, deleted or changed after the variable is created.\n", + "\n", + "* Coordinate (Variables): It is legal for a variable to have the same name as a dimension. Such variables have no special meaning to the netCDF library. However there is a convention that such variables should be treated in a special way by software using this library.\n", + "A variable with the same name as a dimension is called a coordinate variable. It typically defines a physical coordinate corresponding to that dimension.\n", + "\n", + "\n", + "Tips: By definition, coordinates and dimenssions are also variable. For example ds.time is a coordinate but it is also a special variable. But in practice, when we talk about variable, it implies regular/non-coordinate variable. We will use this convention for the remaining notebook. " + ] + }, + { + "cell_type": "markdown", + "id": "d47120f9", + "metadata": {}, + "source": [ + "### Xarray Variable\n", + "\n", + "\n", + "We can use `ds.variables` to access varialbes of a dataset. But many times viewing print-out the whole datasets can be overwhemling. Instead, we would work on the individual variable (also called Xarray data array.) using the `ds[var_name]` syntax, where `var_name` is the name of the variable. See the following examples in action.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5a57e136", + "metadata": {}, + "outputs": [], + "source": [ + "print(type(ds[\"time\"]))\n", + "ds[\"time\"]" + ] + }, + { + "cell_type": "markdown", + "id": "51ec643e", + "metadata": {}, + "source": [ + "#### Variable properties\n", + "For individual varible, the following properties are mostly used with the assocaited xarray method to retrieve them. (assuming var = ds[var_name])\n", + "* name: `var.name`\n", + "* data content: `var.data`\n", + "* attributes: `var.attrs`\n", + "* dimenstions: `var.dims`\n", + "* data type: `var.data.dtype`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a632a525", + "metadata": {}, + "outputs": [], + "source": [ + "var_name = \"time\"\n", + "var = ds[var_name]\n", + "\n", + "print(\"var.name: \\n\", var.name, \"\\n\")\n", + "print(\"var.data: \\n\", var.data, \"\\n\")\n", + "print(\"var.attrs: \\n\", var.attrs, \"\\n\")\n", + "print(\"var.dims: \\n\", var.dims, \"\\n\")\n", + "print(\"var.data.dtype: \\n\", var.data.dtype, \"\\n\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b256b07f", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: indivdual variable info table (collect and display variable info as a tabular format)\n", + "df_info = pd.DataFrame()\n", + "df_info[\"var_name\"] = list(ds.variables)\n", + "df_info[\"dims\"] = df_info.var_name.apply(lambda x: ds[x].dims)\n", + "df_info[\"is_dim\"] = df_info.var_name.apply(lambda x: x in ds.dims)\n", + "df_info[\"n_dim\"] = df_info.var_name.apply(lambda x: len(ds[x].dims))\n", + "df_info[\"attrs\"] = df_info.var_name.apply(lambda x: ds[x].attrs)\n", + "df_info[\"dtype\"] = df_info.var_name.apply(lambda x: ds[x].data.dtype)\n", + "df_info" + ] + }, + { + "cell_type": "markdown", + "id": "e6a36b96", + "metadata": {}, + "source": [ + "### Data cleaning/Preprocessing (skipped)\n", + "Data cleaning and preprocessing is an important stage in the the data analysis pipeline. However it will be out of the scope of this notebook. For more information about data cleaning and preprocessing basics, here are some references. \n", + "* [Xarray Fundamentals](https://earth-env-data-science.github.io/lectures/xarray/xarray.html)\n", + "* [Xarray Tutorial — Pangeo Gallery documentation](http://gallery.pangeo.io/repos/pangeo-data/pangeo-tutorial-gallery/xarray.html)\n", + "* [Pythonic Data Cleaning With pandas and NumPy](https://realpython.com/python-data-cleaning-numpy-pandas/)\n", + "* [Pandas - Cleaning Data](https://www.w3schools.com/python/pandas/pandas_cleaning.asp)" + ] + }, + { + "cell_type": "markdown", + "id": "ae6e1a69", + "metadata": {}, + "source": [ + "### Plotting\n", + "Data visualization (or plotting) is another important data analysis topic and deserves its own discussion. Here in this notebook we will only demonstrate basic tool for simple data visualization tasks.\n", + "\n", + "Here are some reference you might find useful:\n", + "* xarray plotting: https://docs.xarray.dev/en/stable/user-guide/plotting.html\n", + "* Atmospheric Community Toolkit (ACT): https://arm-doe.github.io/ACT/index.html\n", + "\n", + "Note: this notebook is auto-generated using a template. It uses a general idea to select variable(s) to plot and is not customized to each indivdual notebook. Feel free to change the variables in intrrests, especially certain figure is failed to plot." + ] + }, + { + "cell_type": "markdown", + "id": "7e417fb2", + "metadata": {}, + "source": [ + "#### 1-dimenssional basic time series plot\n", + "\n", + "For the following plot we would like to find variables such that\n", + "* it has one and only one dimession\n", + "* \"time\" is its coordinate variable\n", + "* it is not a dimenssion itself,\n", + "* it is not a special variable with substrings within [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "\n", + "Please see the following example in action. (For more details about pandas filtering, please see the following references.)\n", + "* [pandas: multiple conditions while indexing data frame](https://stackoverflow.com/questions/22591174/pandas-multiple-conditions-while-indexing-data-frame-unexpected-behavior)\n", + "* [pandas.DataFrame.apply](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.apply.html)\n", + "* [How to test if a string contains one of the substrings in a list...](https://stackoverflow.com/questions/26577516/how-to-test-if-a-string-contains-one-of-the-substrings-in-a-list-in-pandas" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30edac2d", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==1, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter = df_info[(df_info.n_dim == 1) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4777a659", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_1d = df_filter.var_name.values[0]\n", + " var_1d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb5ae985", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " ds[var_1d].plot()\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "9e41066e", + "metadata": {}, + "source": [ + "#### 2-dimenssional basic plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09bd4adc", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==2, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_2 = df_info[(df_info.n_dim == 2) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2cdea1a9", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_2d = df_filter_2.var_name.values[0]\n", + " var_2d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5f48879", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " print(ds[var_2d].dims)\n", + " # ds[var_2d].plot()\n", + "\n", + " # conventionally, use \"time\" as x-axis\n", + " ds[var_2d].plot(x=\"time\")\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "7554b2a6", + "metadata": {}, + "source": [ + "#### qc-plotting (optional)\n", + "\n", + "Note: act qc plotting has more strict requirement, one of them is the associated qc variable need to have \"flag_masks\" attributes. Which is added by using ds = act.io.armfiles.read_netcdf(files_list), then ds.clean.cleanup()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71c2096f", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " ds_act = act.io.armfiles.read_netcdf(full_path)\n", + " print(type(ds_act))\n", + " ds_act.clean.cleanup()\n", + "\n", + " # or \n", + " # ds.clean.cleanup()\n", + "except Exception as e:\n", + " print(\"ERROR\", e)\n", + " ds_act = ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "844f8505", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter valid variables for ACT qc plotting\n", + "condition = (\"qc_\" + df_info.var_name).apply(lambda x: ds_act[x].attrs.get(\"flag_masks\") is not None \n", + " if x in list(ds.variables) else False)\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_3 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings))) &\n", + " condition\n", + " ]\n", + "df_filter_3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd38cc6", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "try:\n", + " qc_variable = df_filter_3.var_name.values[0]\n", + " print(qc_variable)\n", + "except Exception as e:\n", + " print(e)\n", + " \n", + "try:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + " qc_ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "d76e4d27", + "metadata": {}, + "source": [ + "#### bonus: choose variables to plot from a dropdown menu " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb733804", + "metadata": {}, + "outputs": [], + "source": [ + "# Valid variables filtering\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_4 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82d5c20d", + "metadata": {}, + "outputs": [], + "source": [ + "# example 1: using xarray plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2fc9aaa4", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# fig, ax = plt.subplots(figsize=(10, 4))\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "# fig.clear() # Remove old lines from plot and plot new one\n", + "# if len(ds[var].dims)==2:\n", + "# ds[var].plot(x=\"time\", add_colorbar=False)\n", + "# else:\n", + "# ds[var].plot()\n", + "# plt.grid()\n", + "# plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fabdd802", + "metadata": {}, + "outputs": [], + "source": [ + "# example 2: using act plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "962b4186", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "\n", + "\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "\n", + "# i_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + "# i_display.add_subplots((1,), figsize=(10, 4))\n", + "# ax = i_display.plot(var, subplot_index=(0,), set_title=f\"{var} ({ds_act[var].attrs['long_name']})\",)\n", + "\n", + "# ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "# ax.grid()\n", + "# plt.show()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/DLPROF-WIND/dlprofwind4news.c1.ipynb b/VAPs/quicklook/DLPROF-WIND/dlprofwind4news.c1.ipynb new file mode 100644 index 00000000..6cfd8dfe --- /dev/null +++ b/VAPs/quicklook/DLPROF-WIND/dlprofwind4news.c1.ipynb @@ -0,0 +1,297 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# DLPROFWIND4NEWS.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/dlprof-wind) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'dlprofwind4news'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2017-10-29', 'facility': 'M1', 'site': 'asi', 'start_date': '2016-09-06'}, {'end_date': '2020-06-01', 'facility': 'S2', 'site': 'anx', 'start_date': '2019-12-01'}, {'end_date': '2023-09-12', 'facility': 'C1', 'site': 'ena', 'start_date': '2014-10-21'}, {'end_date': '2022-09-30', 'facility': 'M1', 'site': 'hou', 'start_date': '2021-08-05'}, {'end_date': '2015-08-27', 'facility': 'M1', 'site': 'mao', 'start_date': '2014-01-31'}, {'end_date': '2023-06-15', 'facility': 'M1', 'site': 'guc', 'start_date': '2021-09-01'}, {'end_date': '2019-04-30', 'facility': 'M1', 'site': 'cor', 'start_date': '2018-09-28'}, {'end_date': '2012-03-31', 'facility': 'M1', 'site': 'pgh', 'start_date': '2011-07-21'}, {'end_date': '2023-12-17', 'facility': 'C1', 'site': 'nsa', 'start_date': '2014-07-30'}, {'end_date': '2021-06-14', 'facility': 'M1', 'site': 'oli', 'start_date': '2014-07-28'}, {'end_date': '2023-12-18', 'facility': 'C1', 'site': 'sgp', 'start_date': '2010-11-02'}, {'end_date': '2022-09-26', 'facility': 'E32', 'site': 'sgp', 'start_date': '2016-05-03'}, {'end_date': '2023-10-31', 'facility': 'E37', 'site': 'sgp', 'start_date': '2016-05-03'}, {'end_date': '2023-08-23', 'facility': 'E39', 'site': 'sgp', 'start_date': '2016-04-01'}, {'end_date': '2022-10-30', 'facility': 'E41', 'site': 'sgp', 'start_date': '2016-05-03'}, {'end_date': '2013-06-19', 'facility': 'M1', 'site': 'pvc', 'start_date': '2012-06-29'}, {'end_date': '2015-01-03', 'facility': 'C3', 'site': 'twp', 'start_date': '2010-12-13'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2023-12-16'\n", + "date_end = '2023-12-18'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['u', 'v', 'wind_speed']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'u'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/DLPROF-WSTATS/DLPROF-WSTATS_tutorial.ipynb b/VAPs/quicklook/DLPROF-WSTATS/DLPROF-WSTATS_tutorial.ipynb new file mode 100644 index 00000000..8724b42e --- /dev/null +++ b/VAPs/quicklook/DLPROF-WSTATS/DLPROF-WSTATS_tutorial.ipynb @@ -0,0 +1,838 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# DLPROFWSTATS4NEWS.C1 Notebook\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/dlprof-wstats) for more information about this vap." + ] + }, + { + "cell_type": "markdown", + "id": "97097763", + "metadata": {}, + "source": [ + "In this notebook, we demonstrate the workflow to explore ARM vap data (using dlprofwstats4news as an example.) Value-added products (VAPs) are higher-order data products that have been analyzed and processed to ease scientists’ use of ARM data in atmospheric research and global climate models. \n", + "Here is the main content we will cover." + ] + }, + { + "cell_type": "markdown", + "id": "eddec40f", + "metadata": {}, + "source": [ + "# Table of Content\n", + "## Access the data\n", + "* How to retrieve the data\n", + "* Data path and file name conventions\n", + "* Load data\n", + "## Explore the data\n", + "* NetCDF Data structure\n", + "* Xarray essentials\n", + "* Xarray Variable\n", + "## Plot the data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "import random\n", + "\n", + "import glob\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "2372d5be", + "metadata": {}, + "source": [ + "## Access the data" + ] + }, + { + "cell_type": "markdown", + "id": "5b0d9684", + "metadata": {}, + "source": [ + "### How to retrieve the data\n", + "We assume the path \"/data/archive\" is available where you are running this notebook. You can use `os.path.exists(\"/data/archive\")` to verify if the path exists at your machine.\n", + "\n", + "\n", + "### Data path and file name conventions\n", + "There several common terminologies regarding ARM data, for example, data-stream-name, data-level, site, facility, instrument, etc. (For more details, please see [ARM Data File Standards Version 1.2](https://www.google.com/search?q=arm+datastream+facility+cite+definition&rlz=1C1GCEJ_enUS1029US1029&ei=hb41ZICrBPukqtsPvMODIA&ved=0ahUKEwjAgeiS0aL-AhV7kmoFHbzhAAQQ4dUDCBA&uact=5&oq=arm+datastream+facility+cite+definition&gs_lcp=Cgxnd3Mtd2l6LXNlcnAQAzIFCAAQogQyBQgAEKIEMgUIABCiBDIFCAAQogQyBQgAEKIEOgoIABBHENYEELADOgoIIRCgARDDBBAKSgQIQRgAUJ4DWIoOYO0PaAFwAXgAgAGMAYgBuQmSAQMyLjmYAQCgAQHIAQjAAQE&sclient=gws-wiz-serp).) \n", + "\n", + "For example, this notebook is called `dlprofwstats4news.c1`, where `dlprofwstats4news` is the \"datastream name\", and the `{process.ds_class_level}}` is the \"data level\".\n", + "\n", + "This datastream also contains site `asi` and facility `M1`. (Note: individual datastream might have multiple site-facility pairs.)\n", + "In such a case, the data of this data-stream is stored at `/data/archive/asi/asidlprofwstats4newsM1.c1`, which is in the format of `//.`. We can use the following method to assign the data-stream directory `datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )`\n", + "\n", + "The data files under datastream_dir also follows naming conventions. But once reach the datastream_dir level, the most import file naming convention to differentiate the files is \"yyyyMMdd.hhmmss\", which comes handy to filter out files based on datetime. For example, we can use `glob.glob(f'{datastream_dir}/*.200709*.*')` to filter files in 2007 September.\n", + "\n", + "(Note: refer to the https://adc.arm.gov/discovery/#/ and https://adc.arm.gov/solr8/metadata/select API to explore ARM datastream and assoicated available sites and facilities\n", + "\n", + "Please see the following examples in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d7e9eb85", + "metadata": {}, + "outputs": [], + "source": [ + "# Verify if DATA_DIR path exists\n", + "DATA_DIR = \"/data/archive\"\n", + "os.path.exists(DATA_DIR)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "586993fd", + "metadata": {}, + "outputs": [], + "source": [ + "# Speicify datastream_dir following the path conventions and check its existence\n", + "DATASTREAM_NAME = \"dlprofwstats4news\"\n", + "DATA_LEVEL = \"c1\"\n", + "site = \"asi\"\n", + "facility = \"M1\"\n", + "datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )\n", + "print(datastream_dir)\n", + "print(os.path.exists(datastream_dir))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0742f7c1", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: list 5 (random) files under datastream_dir\n", + "files = os.listdir(datastream_dir)\n", + "files[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39b98a36", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: get most recent file\n", + "list_of_files = glob.glob(f\"{datastream_dir}/*\") # * means all if need specific format then *.csv\n", + "latest_file = max(list_of_files, key=os.path.getctime)\n", + "latest_file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "902d514e", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus sort datastream files based on datetime\n", + "files = os.listdir(datastream_dir)\n", + "file_sorted = files.copy()\n", + "file_sorted.sort() \n", + "print(file_sorted[:5])\n", + "\n", + "# to reverse\n", + "file_sorted_reverse = files.copy() \n", + "file_sorted_reverse.sort(reverse=True)\n", + "print(file_sorted_reverse[:5])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec5923b2", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: pattern matching\n", + "# filter the 200709** files under datastream_dir\n", + "files_filter = glob.glob(f'{datastream_dir}/*.200709*.*')\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c4a4aa18", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: use armnotebook_utils.file_filter (TODO) to filter files based on datastream info \n" + ] + }, + { + "cell_type": "markdown", + "id": "cfeb9efc", + "metadata": {}, + "source": [ + "### Load data\n", + "The arm data is stored in [NetCDF](https://en.wikipedia.org/wiki/NetCDF#:~:text=%22NetCDF%20(network%20Common%20Data%20Form,format%20for%20representing%20scientific%20data.) format. We can use xarray's `open_dataset` method to load single file, or `open_mfdataset` to open multiple files. (Note: the latter will still return a single xarray dataset object by combining multiple files.)\n", + "\n", + "Note: open_dataset keeps the file handle open and lazy loads its contents. All parameters are passed directly to open_dataset. It is a preferable method over load_dataset for memory-effiency. (more details, please see [xarray.open_dataset](https://docs.xarray.dev/en/stable/generated/xarray.open_dataset.html))\n", + "\n", + "See the following example in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a440a329", + "metadata": {}, + "outputs": [], + "source": [ + "# open single file\n", + "full_path = latest_file\n", + "print(full_path)\n", + "ds_single = xr.open_dataset(full_path)\n", + "ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0143a3d", + "metadata": {}, + "outputs": [], + "source": [ + "print(ds_single)\n", + "print(type(ds_single))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c0f8939", + "metadata": {}, + "outputs": [], + "source": [ + "# open multiple files\n", + "n_files = 3\n", + "full_paths = [os.path.join(datastream_dir, f_path) for f_path in file_sorted_reverse[:n_files]]\n", + "print(full_paths)\n", + "try: # Note: sometimes multiple files cannot be merged, so we used try except here.\n", + " ds_mutiple = xr.open_mfdataset(full_paths)\n", + " # ds_mutiple\n", + " print(type(ds_mutiple))\n", + " print(ds_mutiple)\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "3fa59943", + "metadata": {}, + "source": [ + "## Explore the data" + ] + }, + { + "cell_type": "markdown", + "id": "571f69b9", + "metadata": {}, + "source": [ + "### NetCDF Data structure \n", + "\n", + "\n", + "(If you are confident with NetCDF basics and xarray essentials, feel free to skip this session.)\n", + "Before we dive into data exploration, there are some eseential concpets we should be familiar with (shown below): \n", + "* Dataset\n", + "* Data array\n", + "* Variable\n", + "* Dimenssion\n", + "* Coordinate\n", + "* Data Type\n", + "* Meta Data (Attributes)\n", + "\n", + "We will not go into details about NetCDF basics and here are some references you might find helpful\n", + "* [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#)\n", + "* [Network Common Data Form (NetCDF)](https://www.unidata.ucar.edu/software/netcdf/) and [A Brief History of (netCDF) Time](https://www.unidata.ucar.edu/software/netcdf/time/recs.html)\n", + "\n", + "\n", + "\n", + "### Xarray essentials\n", + "Earlier we introduced the xarray pacakge and used `open_dataset` and `open_mfdataset` to retrieve NetCDF data file as an xarray Dataset object. Recall that you can review the data in a notebook by using `print(ds)` or `ds`. \n", + "\n", + "Using xarray to retrieve the aforementioned NetCDF basics is straightforwared. In general,\n", + "* Dataset: `ds`\n", + "* Data array: `ds.variables`\n", + "* Variable: `ds.ds.variables`\n", + "* Dimenssion: `ds.dims`\n", + "* Coordinate: `ds.coords`\n", + "* Data Type: `type`\n", + "* Meta Data (Attributes)\n", + "\n", + "Also, here are some references if you are new to xarray\n", + "* [Xarray in 45 minutes](https://tutorial.xarray.dev/overview/xarray-in-45-min.html)\n", + "* [Handling NetCDF Files using XArray for Absolute Beginners](https://towardsdatascience.com/handling-netcdf-files-using-xarray-for-absolute-beginners-111a8ab4463f)\n", + "\n", + "Try the following commands in action\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b3eac323", + "metadata": {}, + "outputs": [], + "source": [ + "ds = ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77ecf85d", + "metadata": {}, + "outputs": [], + "source": [ + "# Dataset \n", + "ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25e7de09", + "metadata": {}, + "outputs": [], + "source": [ + "# Data array, Variable\n", + "# Note: the info can be overwhelming. Do not attempt to grasp everything at the first glance.\n", + "ds.variables" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c41b67e", + "metadata": {}, + "outputs": [], + "source": [ + "# dimenssions\n", + "ds.dims" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "156f1dfc", + "metadata": {}, + "outputs": [], + "source": [ + "# coordinates\n", + "ds.coords" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "277d6064", + "metadata": {}, + "outputs": [], + "source": [ + "# Meta Data (Attributes)\n", + "ds.attrs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d334681f", + "metadata": {}, + "outputs": [], + "source": [ + "# type\n", + "print(type(ds))\n", + "print(type(ds.variables))\n", + "print(type(ds.dims))\n", + "print(type(ds.coords))\n", + "print(type(ds.attrs))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "643399d6", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: retrieve variable names only\n", + "list(ds.variables)" + ] + }, + { + "cell_type": "markdown", + "id": "1cf36878", + "metadata": {}, + "source": [ + "#### Discussion: variable vs. coordinates vs. dimenssions. \n", + "From [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#):\n", + "\n", + "* Dimession: A dimension may be used to represent a real physical dimension, for example, time, latitude, longitude, or height. A dimension might also be used to index other quantities, for example station or model-run-number.\n", + "A netCDF dimension has both a name and a length. A dimension length is an arbitrary positive integer, except that one dimension in a netCDF dataset can have the length UNLIMITED.\n", + "\n", + "* Variables: Variables are used to store the bulk of the data in a netCDF dataset. A variable represents an array of values of the same type. A scalar value is treated as a 0-dimensional array. A variable has a name, a data type, and a shape described by its list of dimensions specified when the variable is created. A variable may also have associated attributes, which may be added, deleted or changed after the variable is created.\n", + "\n", + "* Coordinate (Variables): It is legal for a variable to have the same name as a dimension. Such variables have no special meaning to the netCDF library. However there is a convention that such variables should be treated in a special way by software using this library.\n", + "A variable with the same name as a dimension is called a coordinate variable. It typically defines a physical coordinate corresponding to that dimension.\n", + "\n", + "\n", + "Tips: By definition, coordinates and dimenssions are also variable. For example ds.time is a coordinate but it is also a special variable. But in practice, when we talk about variable, it implies regular/non-coordinate variable. We will use this convention for the remaining notebook. " + ] + }, + { + "cell_type": "markdown", + "id": "d47120f9", + "metadata": {}, + "source": [ + "### Xarray Variable\n", + "\n", + "\n", + "We can use `ds.variables` to access varialbes of a dataset. But many times viewing print-out the whole datasets can be overwhemling. Instead, we would work on the individual variable (also called Xarray data array.) using the `ds[var_name]` syntax, where `var_name` is the name of the variable. See the following examples in action.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5a57e136", + "metadata": {}, + "outputs": [], + "source": [ + "print(type(ds[\"time\"]))\n", + "ds[\"time\"]" + ] + }, + { + "cell_type": "markdown", + "id": "51ec643e", + "metadata": {}, + "source": [ + "#### Variable properties\n", + "For individual varible, the following properties are mostly used with the assocaited xarray method to retrieve them. (assuming var = ds[var_name])\n", + "* name: `var.name`\n", + "* data content: `var.data`\n", + "* attributes: `var.attrs`\n", + "* dimenstions: `var.dims`\n", + "* data type: `var.data.dtype`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a632a525", + "metadata": {}, + "outputs": [], + "source": [ + "var_name = \"time\"\n", + "var = ds[var_name]\n", + "\n", + "print(\"var.name: \\n\", var.name, \"\\n\")\n", + "print(\"var.data: \\n\", var.data, \"\\n\")\n", + "print(\"var.attrs: \\n\", var.attrs, \"\\n\")\n", + "print(\"var.dims: \\n\", var.dims, \"\\n\")\n", + "print(\"var.data.dtype: \\n\", var.data.dtype, \"\\n\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b256b07f", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: indivdual variable info table (collect and display variable info as a tabular format)\n", + "df_info = pd.DataFrame()\n", + "df_info[\"var_name\"] = list(ds.variables)\n", + "df_info[\"dims\"] = df_info.var_name.apply(lambda x: ds[x].dims)\n", + "df_info[\"is_dim\"] = df_info.var_name.apply(lambda x: x in ds.dims)\n", + "df_info[\"n_dim\"] = df_info.var_name.apply(lambda x: len(ds[x].dims))\n", + "df_info[\"attrs\"] = df_info.var_name.apply(lambda x: ds[x].attrs)\n", + "df_info[\"dtype\"] = df_info.var_name.apply(lambda x: ds[x].data.dtype)\n", + "df_info" + ] + }, + { + "cell_type": "markdown", + "id": "e6a36b96", + "metadata": {}, + "source": [ + "### Data cleaning/Preprocessing (skipped)\n", + "Data cleaning and preprocessing is an important stage in the the data analysis pipeline. However it will be out of the scope of this notebook. For more information about data cleaning and preprocessing basics, here are some references. \n", + "* [Xarray Fundamentals](https://earth-env-data-science.github.io/lectures/xarray/xarray.html)\n", + "* [Xarray Tutorial — Pangeo Gallery documentation](http://gallery.pangeo.io/repos/pangeo-data/pangeo-tutorial-gallery/xarray.html)\n", + "* [Pythonic Data Cleaning With pandas and NumPy](https://realpython.com/python-data-cleaning-numpy-pandas/)\n", + "* [Pandas - Cleaning Data](https://www.w3schools.com/python/pandas/pandas_cleaning.asp)" + ] + }, + { + "cell_type": "markdown", + "id": "ae6e1a69", + "metadata": {}, + "source": [ + "### Plotting\n", + "Data visualization (or plotting) is another important data analysis topic and deserves its own discussion. Here in this notebook we will only demonstrate basic tool for simple data visualization tasks.\n", + "\n", + "Here are some reference you might find useful:\n", + "* xarray plotting: https://docs.xarray.dev/en/stable/user-guide/plotting.html\n", + "* Atmospheric Community Toolkit (ACT): https://arm-doe.github.io/ACT/index.html\n", + "\n", + "Note: this notebook is auto-generated using a template. It uses a general idea to select variable(s) to plot and is not customized to each indivdual notebook. Feel free to change the variables in intrrests, especially certain figure is failed to plot." + ] + }, + { + "cell_type": "markdown", + "id": "7e417fb2", + "metadata": {}, + "source": [ + "#### 1-dimenssional basic time series plot\n", + "\n", + "For the following plot we would like to find variables such that\n", + "* it has one and only one dimession\n", + "* \"time\" is its coordinate variable\n", + "* it is not a dimenssion itself,\n", + "* it is not a special variable with substrings within [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "\n", + "Please see the following example in action. (For more details about pandas filtering, please see the following references.)\n", + "* [pandas: multiple conditions while indexing data frame](https://stackoverflow.com/questions/22591174/pandas-multiple-conditions-while-indexing-data-frame-unexpected-behavior)\n", + "* [pandas.DataFrame.apply](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.apply.html)\n", + "* [How to test if a string contains one of the substrings in a list...](https://stackoverflow.com/questions/26577516/how-to-test-if-a-string-contains-one-of-the-substrings-in-a-list-in-pandas" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30edac2d", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==1, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter = df_info[(df_info.n_dim == 1) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4777a659", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_1d = df_filter.var_name.values[0]\n", + " var_1d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb5ae985", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " ds[var_1d].plot()\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "9e41066e", + "metadata": {}, + "source": [ + "#### 2-dimenssional basic plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09bd4adc", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==2, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_2 = df_info[(df_info.n_dim == 2) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2cdea1a9", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_2d = df_filter_2.var_name.values[0]\n", + " var_2d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5f48879", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " print(ds[var_2d].dims)\n", + " # ds[var_2d].plot()\n", + "\n", + " # conventionally, use \"time\" as x-axis\n", + " ds[var_2d].plot(x=\"time\")\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "7554b2a6", + "metadata": {}, + "source": [ + "#### qc-plotting (optional)\n", + "\n", + "Note: act qc plotting has more strict requirement, one of them is the associated qc variable need to have \"flag_masks\" attributes. Which is added by using ds = act.io.armfiles.read_netcdf(files_list), then ds.clean.cleanup()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71c2096f", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " ds_act = act.io.armfiles.read_netcdf(full_path)\n", + " print(type(ds_act))\n", + " ds_act.clean.cleanup()\n", + "\n", + " # or \n", + " # ds.clean.cleanup()\n", + "except Exception as e:\n", + " print(\"ERROR\", e)\n", + " ds_act = ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "844f8505", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter valid variables for ACT qc plotting\n", + "condition = (\"qc_\" + df_info.var_name).apply(lambda x: ds_act[x].attrs.get(\"flag_masks\") is not None \n", + " if x in list(ds.variables) else False)\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_3 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings))) &\n", + " condition\n", + " ]\n", + "df_filter_3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd38cc6", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "try:\n", + " qc_variable = df_filter_3.var_name.values[0]\n", + " print(qc_variable)\n", + "except Exception as e:\n", + " print(e)\n", + " \n", + "try:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + " qc_ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "d76e4d27", + "metadata": {}, + "source": [ + "#### bonus: choose variables to plot from a dropdown menu " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb733804", + "metadata": {}, + "outputs": [], + "source": [ + "# Valid variables filtering\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_4 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82d5c20d", + "metadata": {}, + "outputs": [], + "source": [ + "# example 1: using xarray plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2fc9aaa4", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# fig, ax = plt.subplots(figsize=(10, 4))\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "# fig.clear() # Remove old lines from plot and plot new one\n", + "# if len(ds[var].dims)==2:\n", + "# ds[var].plot(x=\"time\", add_colorbar=False)\n", + "# else:\n", + "# ds[var].plot()\n", + "# plt.grid()\n", + "# plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fabdd802", + "metadata": {}, + "outputs": [], + "source": [ + "# example 2: using act plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "962b4186", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "\n", + "\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "\n", + "# i_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + "# i_display.add_subplots((1,), figsize=(10, 4))\n", + "# ax = i_display.plot(var, subplot_index=(0,), set_title=f\"{var} ({ds_act[var].attrs['long_name']})\",)\n", + "\n", + "# ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "# ax.grid()\n", + "# plt.show()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/DLPROF-WSTATS/dlprofwstats4news.c1.ipynb b/VAPs/quicklook/DLPROF-WSTATS/dlprofwstats4news.c1.ipynb new file mode 100644 index 00000000..68dd8077 --- /dev/null +++ b/VAPs/quicklook/DLPROF-WSTATS/dlprofwstats4news.c1.ipynb @@ -0,0 +1,297 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# DLPROFWSTATS4NEWS.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/dlprof-wstats) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'dlprofwstats4news'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2017-10-30', 'facility': 'M1', 'site': 'asi', 'start_date': '2016-09-06'}, {'end_date': '2020-06-01', 'facility': 'S2', 'site': 'anx', 'start_date': '2019-12-01'}, {'end_date': '2023-09-28', 'facility': 'C1', 'site': 'ena', 'start_date': '2014-10-21'}, {'end_date': '2022-09-29', 'facility': 'M1', 'site': 'hou', 'start_date': '2021-08-05'}, {'end_date': '2015-08-26', 'facility': 'M1', 'site': 'mao', 'start_date': '2014-01-01'}, {'end_date': '2023-06-14', 'facility': 'M1', 'site': 'guc', 'start_date': '2021-09-01'}, {'end_date': '2019-04-29', 'facility': 'M1', 'site': 'cor', 'start_date': '2018-09-23'}, {'end_date': '2012-03-30', 'facility': 'M1', 'site': 'pgh', 'start_date': '2011-06-21'}, {'end_date': '2023-12-17', 'facility': 'C1', 'site': 'nsa', 'start_date': '2014-07-30'}, {'end_date': '2021-06-13', 'facility': 'M1', 'site': 'oli', 'start_date': '2014-07-28'}, {'end_date': '2023-10-23', 'facility': 'C1', 'site': 'sgp', 'start_date': '2010-10-22'}, {'end_date': '2022-09-25', 'facility': 'E32', 'site': 'sgp', 'start_date': '2016-05-03'}, {'end_date': '2023-10-30', 'facility': 'E37', 'site': 'sgp', 'start_date': '2016-05-03'}, {'end_date': '2023-08-22', 'facility': 'E39', 'site': 'sgp', 'start_date': '2016-04-01'}, {'end_date': '2022-10-29', 'facility': 'E41', 'site': 'sgp', 'start_date': '2016-05-03'}, {'end_date': '2013-06-18', 'facility': 'M1', 'site': 'pvc', 'start_date': '2012-06-29'}, {'end_date': '2015-01-04', 'facility': 'C3', 'site': 'twp', 'start_date': '2010-12-13'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2023-10-21'\n", + "date_end = '2023-10-23'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['w_variance', 'w_skewness', 'w_kurtosis']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'w_variance'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/FCDP-AIR/FCDP-AIR_tutorial.ipynb b/VAPs/quicklook/FCDP-AIR/FCDP-AIR_tutorial.ipynb new file mode 100644 index 00000000..efe3ad1b --- /dev/null +++ b/VAPs/quicklook/FCDP-AIR/FCDP-AIR_tutorial.ipynb @@ -0,0 +1,838 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# AAFFCDP.C1 Notebook\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/fcdp-air) for more information about this vap." + ] + }, + { + "cell_type": "markdown", + "id": "97097763", + "metadata": {}, + "source": [ + "In this notebook, we demonstrate the workflow to explore ARM vap data (using aaffcdp as an example.) Value-added products (VAPs) are higher-order data products that have been analyzed and processed to ease scientists’ use of ARM data in atmospheric research and global climate models. \n", + "Here is the main content we will cover." + ] + }, + { + "cell_type": "markdown", + "id": "eddec40f", + "metadata": {}, + "source": [ + "# Table of Content\n", + "## Access the data\n", + "* How to retrieve the data\n", + "* Data path and file name conventions\n", + "* Load data\n", + "## Explore the data\n", + "* NetCDF Data structure\n", + "* Xarray essentials\n", + "* Xarray Variable\n", + "## Plot the data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "import random\n", + "\n", + "import glob\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "2372d5be", + "metadata": {}, + "source": [ + "## Access the data" + ] + }, + { + "cell_type": "markdown", + "id": "5b0d9684", + "metadata": {}, + "source": [ + "### How to retrieve the data\n", + "We assume the path \"/data/archive\" is available where you are running this notebook. You can use `os.path.exists(\"/data/archive\")` to verify if the path exists at your machine.\n", + "\n", + "\n", + "### Data path and file name conventions\n", + "There several common terminologies regarding ARM data, for example, data-stream-name, data-level, site, facility, instrument, etc. (For more details, please see [ARM Data File Standards Version 1.2](https://www.google.com/search?q=arm+datastream+facility+cite+definition&rlz=1C1GCEJ_enUS1029US1029&ei=hb41ZICrBPukqtsPvMODIA&ved=0ahUKEwjAgeiS0aL-AhV7kmoFHbzhAAQQ4dUDCBA&uact=5&oq=arm+datastream+facility+cite+definition&gs_lcp=Cgxnd3Mtd2l6LXNlcnAQAzIFCAAQogQyBQgAEKIEMgUIABCiBDIFCAAQogQyBQgAEKIEOgoIABBHENYEELADOgoIIRCgARDDBBAKSgQIQRgAUJ4DWIoOYO0PaAFwAXgAgAGMAYgBuQmSAQMyLjmYAQCgAQHIAQjAAQE&sclient=gws-wiz-serp).) \n", + "\n", + "For example, this notebook is called `aaffcdp.c1`, where `aaffcdp` is the \"datastream name\", and the `{process.ds_class_level}}` is the \"data level\".\n", + "\n", + "This datastream also contains site `cor` and facility `F1`. (Note: individual datastream might have multiple site-facility pairs.)\n", + "In such a case, the data of this data-stream is stored at `/data/archive/cor/coraaffcdpF1.c1`, which is in the format of `//.`. We can use the following method to assign the data-stream directory `datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )`\n", + "\n", + "The data files under datastream_dir also follows naming conventions. But once reach the datastream_dir level, the most import file naming convention to differentiate the files is \"yyyyMMdd.hhmmss\", which comes handy to filter out files based on datetime. For example, we can use `glob.glob(f'{datastream_dir}/*.200709*.*')` to filter files in 2007 September.\n", + "\n", + "(Note: refer to the https://adc.arm.gov/discovery/#/ and https://adc.arm.gov/solr8/metadata/select API to explore ARM datastream and assoicated available sites and facilities\n", + "\n", + "Please see the following examples in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d7e9eb85", + "metadata": {}, + "outputs": [], + "source": [ + "# Verify if DATA_DIR path exists\n", + "DATA_DIR = \"/data/archive\"\n", + "os.path.exists(DATA_DIR)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "586993fd", + "metadata": {}, + "outputs": [], + "source": [ + "# Speicify datastream_dir following the path conventions and check its existence\n", + "DATASTREAM_NAME = \"aaffcdp\"\n", + "DATA_LEVEL = \"c1\"\n", + "site = \"cor\"\n", + "facility = \"F1\"\n", + "datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )\n", + "print(datastream_dir)\n", + "print(os.path.exists(datastream_dir))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0742f7c1", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: list 5 (random) files under datastream_dir\n", + "files = os.listdir(datastream_dir)\n", + "files[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39b98a36", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: get most recent file\n", + "list_of_files = glob.glob(f\"{datastream_dir}/*\") # * means all if need specific format then *.csv\n", + "latest_file = max(list_of_files, key=os.path.getctime)\n", + "latest_file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "902d514e", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus sort datastream files based on datetime\n", + "files = os.listdir(datastream_dir)\n", + "file_sorted = files.copy()\n", + "file_sorted.sort() \n", + "print(file_sorted[:5])\n", + "\n", + "# to reverse\n", + "file_sorted_reverse = files.copy() \n", + "file_sorted_reverse.sort(reverse=True)\n", + "print(file_sorted_reverse[:5])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec5923b2", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: pattern matching\n", + "# filter the 200709** files under datastream_dir\n", + "files_filter = glob.glob(f'{datastream_dir}/*.200709*.*')\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c4a4aa18", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: use armnotebook_utils.file_filter (TODO) to filter files based on datastream info \n" + ] + }, + { + "cell_type": "markdown", + "id": "cfeb9efc", + "metadata": {}, + "source": [ + "### Load data\n", + "The arm data is stored in [NetCDF](https://en.wikipedia.org/wiki/NetCDF#:~:text=%22NetCDF%20(network%20Common%20Data%20Form,format%20for%20representing%20scientific%20data.) format. We can use xarray's `open_dataset` method to load single file, or `open_mfdataset` to open multiple files. (Note: the latter will still return a single xarray dataset object by combining multiple files.)\n", + "\n", + "Note: open_dataset keeps the file handle open and lazy loads its contents. All parameters are passed directly to open_dataset. It is a preferable method over load_dataset for memory-effiency. (more details, please see [xarray.open_dataset](https://docs.xarray.dev/en/stable/generated/xarray.open_dataset.html))\n", + "\n", + "See the following example in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a440a329", + "metadata": {}, + "outputs": [], + "source": [ + "# open single file\n", + "full_path = latest_file\n", + "print(full_path)\n", + "ds_single = xr.open_dataset(full_path)\n", + "ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0143a3d", + "metadata": {}, + "outputs": [], + "source": [ + "print(ds_single)\n", + "print(type(ds_single))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c0f8939", + "metadata": {}, + "outputs": [], + "source": [ + "# open multiple files\n", + "n_files = 3\n", + "full_paths = [os.path.join(datastream_dir, f_path) for f_path in file_sorted_reverse[:n_files]]\n", + "print(full_paths)\n", + "try: # Note: sometimes multiple files cannot be merged, so we used try except here.\n", + " ds_mutiple = xr.open_mfdataset(full_paths)\n", + " # ds_mutiple\n", + " print(type(ds_mutiple))\n", + " print(ds_mutiple)\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "3fa59943", + "metadata": {}, + "source": [ + "## Explore the data" + ] + }, + { + "cell_type": "markdown", + "id": "571f69b9", + "metadata": {}, + "source": [ + "### NetCDF Data structure \n", + "\n", + "\n", + "(If you are confident with NetCDF basics and xarray essentials, feel free to skip this session.)\n", + "Before we dive into data exploration, there are some eseential concpets we should be familiar with (shown below): \n", + "* Dataset\n", + "* Data array\n", + "* Variable\n", + "* Dimenssion\n", + "* Coordinate\n", + "* Data Type\n", + "* Meta Data (Attributes)\n", + "\n", + "We will not go into details about NetCDF basics and here are some references you might find helpful\n", + "* [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#)\n", + "* [Network Common Data Form (NetCDF)](https://www.unidata.ucar.edu/software/netcdf/) and [A Brief History of (netCDF) Time](https://www.unidata.ucar.edu/software/netcdf/time/recs.html)\n", + "\n", + "\n", + "\n", + "### Xarray essentials\n", + "Earlier we introduced the xarray pacakge and used `open_dataset` and `open_mfdataset` to retrieve NetCDF data file as an xarray Dataset object. Recall that you can review the data in a notebook by using `print(ds)` or `ds`. \n", + "\n", + "Using xarray to retrieve the aforementioned NetCDF basics is straightforwared. In general,\n", + "* Dataset: `ds`\n", + "* Data array: `ds.variables`\n", + "* Variable: `ds.ds.variables`\n", + "* Dimenssion: `ds.dims`\n", + "* Coordinate: `ds.coords`\n", + "* Data Type: `type`\n", + "* Meta Data (Attributes)\n", + "\n", + "Also, here are some references if you are new to xarray\n", + "* [Xarray in 45 minutes](https://tutorial.xarray.dev/overview/xarray-in-45-min.html)\n", + "* [Handling NetCDF Files using XArray for Absolute Beginners](https://towardsdatascience.com/handling-netcdf-files-using-xarray-for-absolute-beginners-111a8ab4463f)\n", + "\n", + "Try the following commands in action\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b3eac323", + "metadata": {}, + "outputs": [], + "source": [ + "ds = ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77ecf85d", + "metadata": {}, + "outputs": [], + "source": [ + "# Dataset \n", + "ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25e7de09", + "metadata": {}, + "outputs": [], + "source": [ + "# Data array, Variable\n", + "# Note: the info can be overwhelming. Do not attempt to grasp everything at the first glance.\n", + "ds.variables" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c41b67e", + "metadata": {}, + "outputs": [], + "source": [ + "# dimenssions\n", + "ds.dims" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "156f1dfc", + "metadata": {}, + "outputs": [], + "source": [ + "# coordinates\n", + "ds.coords" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "277d6064", + "metadata": {}, + "outputs": [], + "source": [ + "# Meta Data (Attributes)\n", + "ds.attrs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d334681f", + "metadata": {}, + "outputs": [], + "source": [ + "# type\n", + "print(type(ds))\n", + "print(type(ds.variables))\n", + "print(type(ds.dims))\n", + "print(type(ds.coords))\n", + "print(type(ds.attrs))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "643399d6", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: retrieve variable names only\n", + "list(ds.variables)" + ] + }, + { + "cell_type": "markdown", + "id": "1cf36878", + "metadata": {}, + "source": [ + "#### Discussion: variable vs. coordinates vs. dimenssions. \n", + "From [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#):\n", + "\n", + "* Dimession: A dimension may be used to represent a real physical dimension, for example, time, latitude, longitude, or height. A dimension might also be used to index other quantities, for example station or model-run-number.\n", + "A netCDF dimension has both a name and a length. A dimension length is an arbitrary positive integer, except that one dimension in a netCDF dataset can have the length UNLIMITED.\n", + "\n", + "* Variables: Variables are used to store the bulk of the data in a netCDF dataset. A variable represents an array of values of the same type. A scalar value is treated as a 0-dimensional array. A variable has a name, a data type, and a shape described by its list of dimensions specified when the variable is created. A variable may also have associated attributes, which may be added, deleted or changed after the variable is created.\n", + "\n", + "* Coordinate (Variables): It is legal for a variable to have the same name as a dimension. Such variables have no special meaning to the netCDF library. However there is a convention that such variables should be treated in a special way by software using this library.\n", + "A variable with the same name as a dimension is called a coordinate variable. It typically defines a physical coordinate corresponding to that dimension.\n", + "\n", + "\n", + "Tips: By definition, coordinates and dimenssions are also variable. For example ds.time is a coordinate but it is also a special variable. But in practice, when we talk about variable, it implies regular/non-coordinate variable. We will use this convention for the remaining notebook. " + ] + }, + { + "cell_type": "markdown", + "id": "d47120f9", + "metadata": {}, + "source": [ + "### Xarray Variable\n", + "\n", + "\n", + "We can use `ds.variables` to access varialbes of a dataset. But many times viewing print-out the whole datasets can be overwhemling. Instead, we would work on the individual variable (also called Xarray data array.) using the `ds[var_name]` syntax, where `var_name` is the name of the variable. See the following examples in action.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5a57e136", + "metadata": {}, + "outputs": [], + "source": [ + "print(type(ds[\"time\"]))\n", + "ds[\"time\"]" + ] + }, + { + "cell_type": "markdown", + "id": "51ec643e", + "metadata": {}, + "source": [ + "#### Variable properties\n", + "For individual varible, the following properties are mostly used with the assocaited xarray method to retrieve them. (assuming var = ds[var_name])\n", + "* name: `var.name`\n", + "* data content: `var.data`\n", + "* attributes: `var.attrs`\n", + "* dimenstions: `var.dims`\n", + "* data type: `var.data.dtype`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a632a525", + "metadata": {}, + "outputs": [], + "source": [ + "var_name = \"time\"\n", + "var = ds[var_name]\n", + "\n", + "print(\"var.name: \\n\", var.name, \"\\n\")\n", + "print(\"var.data: \\n\", var.data, \"\\n\")\n", + "print(\"var.attrs: \\n\", var.attrs, \"\\n\")\n", + "print(\"var.dims: \\n\", var.dims, \"\\n\")\n", + "print(\"var.data.dtype: \\n\", var.data.dtype, \"\\n\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b256b07f", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: indivdual variable info table (collect and display variable info as a tabular format)\n", + "df_info = pd.DataFrame()\n", + "df_info[\"var_name\"] = list(ds.variables)\n", + "df_info[\"dims\"] = df_info.var_name.apply(lambda x: ds[x].dims)\n", + "df_info[\"is_dim\"] = df_info.var_name.apply(lambda x: x in ds.dims)\n", + "df_info[\"n_dim\"] = df_info.var_name.apply(lambda x: len(ds[x].dims))\n", + "df_info[\"attrs\"] = df_info.var_name.apply(lambda x: ds[x].attrs)\n", + "df_info[\"dtype\"] = df_info.var_name.apply(lambda x: ds[x].data.dtype)\n", + "df_info" + ] + }, + { + "cell_type": "markdown", + "id": "e6a36b96", + "metadata": {}, + "source": [ + "### Data cleaning/Preprocessing (skipped)\n", + "Data cleaning and preprocessing is an important stage in the the data analysis pipeline. However it will be out of the scope of this notebook. For more information about data cleaning and preprocessing basics, here are some references. \n", + "* [Xarray Fundamentals](https://earth-env-data-science.github.io/lectures/xarray/xarray.html)\n", + "* [Xarray Tutorial — Pangeo Gallery documentation](http://gallery.pangeo.io/repos/pangeo-data/pangeo-tutorial-gallery/xarray.html)\n", + "* [Pythonic Data Cleaning With pandas and NumPy](https://realpython.com/python-data-cleaning-numpy-pandas/)\n", + "* [Pandas - Cleaning Data](https://www.w3schools.com/python/pandas/pandas_cleaning.asp)" + ] + }, + { + "cell_type": "markdown", + "id": "ae6e1a69", + "metadata": {}, + "source": [ + "### Plotting\n", + "Data visualization (or plotting) is another important data analysis topic and deserves its own discussion. Here in this notebook we will only demonstrate basic tool for simple data visualization tasks.\n", + "\n", + "Here are some reference you might find useful:\n", + "* xarray plotting: https://docs.xarray.dev/en/stable/user-guide/plotting.html\n", + "* Atmospheric Community Toolkit (ACT): https://arm-doe.github.io/ACT/index.html\n", + "\n", + "Note: this notebook is auto-generated using a template. It uses a general idea to select variable(s) to plot and is not customized to each indivdual notebook. Feel free to change the variables in intrrests, especially certain figure is failed to plot." + ] + }, + { + "cell_type": "markdown", + "id": "7e417fb2", + "metadata": {}, + "source": [ + "#### 1-dimenssional basic time series plot\n", + "\n", + "For the following plot we would like to find variables such that\n", + "* it has one and only one dimession\n", + "* \"time\" is its coordinate variable\n", + "* it is not a dimenssion itself,\n", + "* it is not a special variable with substrings within [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "\n", + "Please see the following example in action. (For more details about pandas filtering, please see the following references.)\n", + "* [pandas: multiple conditions while indexing data frame](https://stackoverflow.com/questions/22591174/pandas-multiple-conditions-while-indexing-data-frame-unexpected-behavior)\n", + "* [pandas.DataFrame.apply](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.apply.html)\n", + "* [How to test if a string contains one of the substrings in a list...](https://stackoverflow.com/questions/26577516/how-to-test-if-a-string-contains-one-of-the-substrings-in-a-list-in-pandas" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30edac2d", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==1, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter = df_info[(df_info.n_dim == 1) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4777a659", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_1d = df_filter.var_name.values[0]\n", + " var_1d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb5ae985", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " ds[var_1d].plot()\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "9e41066e", + "metadata": {}, + "source": [ + "#### 2-dimenssional basic plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09bd4adc", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==2, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_2 = df_info[(df_info.n_dim == 2) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2cdea1a9", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_2d = df_filter_2.var_name.values[0]\n", + " var_2d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5f48879", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " print(ds[var_2d].dims)\n", + " # ds[var_2d].plot()\n", + "\n", + " # conventionally, use \"time\" as x-axis\n", + " ds[var_2d].plot(x=\"time\")\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "7554b2a6", + "metadata": {}, + "source": [ + "#### qc-plotting (optional)\n", + "\n", + "Note: act qc plotting has more strict requirement, one of them is the associated qc variable need to have \"flag_masks\" attributes. Which is added by using ds = act.io.armfiles.read_netcdf(files_list), then ds.clean.cleanup()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71c2096f", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " ds_act = act.io.armfiles.read_netcdf(full_path)\n", + " print(type(ds_act))\n", + " ds_act.clean.cleanup()\n", + "\n", + " # or \n", + " # ds.clean.cleanup()\n", + "except Exception as e:\n", + " print(\"ERROR\", e)\n", + " ds_act = ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "844f8505", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter valid variables for ACT qc plotting\n", + "condition = (\"qc_\" + df_info.var_name).apply(lambda x: ds_act[x].attrs.get(\"flag_masks\") is not None \n", + " if x in list(ds.variables) else False)\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_3 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings))) &\n", + " condition\n", + " ]\n", + "df_filter_3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd38cc6", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "try:\n", + " qc_variable = df_filter_3.var_name.values[0]\n", + " print(qc_variable)\n", + "except Exception as e:\n", + " print(e)\n", + " \n", + "try:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + " qc_ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "d76e4d27", + "metadata": {}, + "source": [ + "#### bonus: choose variables to plot from a dropdown menu " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb733804", + "metadata": {}, + "outputs": [], + "source": [ + "# Valid variables filtering\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_4 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82d5c20d", + "metadata": {}, + "outputs": [], + "source": [ + "# example 1: using xarray plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2fc9aaa4", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# fig, ax = plt.subplots(figsize=(10, 4))\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "# fig.clear() # Remove old lines from plot and plot new one\n", + "# if len(ds[var].dims)==2:\n", + "# ds[var].plot(x=\"time\", add_colorbar=False)\n", + "# else:\n", + "# ds[var].plot()\n", + "# plt.grid()\n", + "# plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fabdd802", + "metadata": {}, + "outputs": [], + "source": [ + "# example 2: using act plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "962b4186", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "\n", + "\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "\n", + "# i_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + "# i_display.add_subplots((1,), figsize=(10, 4))\n", + "# ax = i_display.plot(var, subplot_index=(0,), set_title=f\"{var} ({ds_act[var].attrs['long_name']})\",)\n", + "\n", + "# ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "# ax.grid()\n", + "# plt.show()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/FCDP-AIR/aaffcdp.c1.ipynb b/VAPs/quicklook/FCDP-AIR/aaffcdp.c1.ipynb new file mode 100644 index 00000000..013560ce --- /dev/null +++ b/VAPs/quicklook/FCDP-AIR/aaffcdp.c1.ipynb @@ -0,0 +1,297 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# AAFFCDP.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/fcdp-air) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'aaffcdp'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2018-12-08', 'facility': 'F1', 'site': 'cor', 'start_date': '2018-11-04'}, {'end_date': '2018-02-19', 'facility': 'F1', 'site': 'ena', 'start_date': '2017-06-21'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'cor', 'F1' )\n", + "\n", + "date_start = '2018-12-06'\n", + "date_end = '2018-12-08'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['total_number_concentration', 'number_concentration']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'total_number_concentration'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/GVR/GVR_tutorial.ipynb b/VAPs/quicklook/GVR/GVR_tutorial.ipynb new file mode 100644 index 00000000..d3ad529a --- /dev/null +++ b/VAPs/quicklook/GVR/GVR_tutorial.ipynb @@ -0,0 +1,838 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# GVR.C1 Notebook\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/gvr) for more information about this vap." + ] + }, + { + "cell_type": "markdown", + "id": "97097763", + "metadata": {}, + "source": [ + "In this notebook, we demonstrate the workflow to explore ARM vap data (using gvr as an example.) Value-added products (VAPs) are higher-order data products that have been analyzed and processed to ease scientists’ use of ARM data in atmospheric research and global climate models. \n", + "Here is the main content we will cover." + ] + }, + { + "cell_type": "markdown", + "id": "eddec40f", + "metadata": {}, + "source": [ + "# Table of Content\n", + "## Access the data\n", + "* How to retrieve the data\n", + "* Data path and file name conventions\n", + "* Load data\n", + "## Explore the data\n", + "* NetCDF Data structure\n", + "* Xarray essentials\n", + "* Xarray Variable\n", + "## Plot the data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "import random\n", + "\n", + "import glob\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "2372d5be", + "metadata": {}, + "source": [ + "## Access the data" + ] + }, + { + "cell_type": "markdown", + "id": "5b0d9684", + "metadata": {}, + "source": [ + "### How to retrieve the data\n", + "We assume the path \"/data/archive\" is available where you are running this notebook. You can use `os.path.exists(\"/data/archive\")` to verify if the path exists at your machine.\n", + "\n", + "\n", + "### Data path and file name conventions\n", + "There several common terminologies regarding ARM data, for example, data-stream-name, data-level, site, facility, instrument, etc. (For more details, please see [ARM Data File Standards Version 1.2](https://www.google.com/search?q=arm+datastream+facility+cite+definition&rlz=1C1GCEJ_enUS1029US1029&ei=hb41ZICrBPukqtsPvMODIA&ved=0ahUKEwjAgeiS0aL-AhV7kmoFHbzhAAQQ4dUDCBA&uact=5&oq=arm+datastream+facility+cite+definition&gs_lcp=Cgxnd3Mtd2l6LXNlcnAQAzIFCAAQogQyBQgAEKIEMgUIABCiBDIFCAAQogQyBQgAEKIEOgoIABBHENYEELADOgoIIRCgARDDBBAKSgQIQRgAUJ4DWIoOYO0PaAFwAXgAgAGMAYgBuQmSAQMyLjmYAQCgAQHIAQjAAQE&sclient=gws-wiz-serp).) \n", + "\n", + "For example, this notebook is called `gvr.c1`, where `gvr` is the \"datastream name\", and the `{process.ds_class_level}}` is the \"data level\".\n", + "\n", + "This datastream also contains site `nsa` and facility `C1`. (Note: individual datastream might have multiple site-facility pairs.)\n", + "In such a case, the data of this data-stream is stored at `/data/archive/nsa/nsagvrC1.c1`, which is in the format of `//.`. We can use the following method to assign the data-stream directory `datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )`\n", + "\n", + "The data files under datastream_dir also follows naming conventions. But once reach the datastream_dir level, the most import file naming convention to differentiate the files is \"yyyyMMdd.hhmmss\", which comes handy to filter out files based on datetime. For example, we can use `glob.glob(f'{datastream_dir}/*.200709*.*')` to filter files in 2007 September.\n", + "\n", + "(Note: refer to the https://adc.arm.gov/discovery/#/ and https://adc.arm.gov/solr8/metadata/select API to explore ARM datastream and assoicated available sites and facilities\n", + "\n", + "Please see the following examples in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d7e9eb85", + "metadata": {}, + "outputs": [], + "source": [ + "# Verify if DATA_DIR path exists\n", + "DATA_DIR = \"/data/archive\"\n", + "os.path.exists(DATA_DIR)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "586993fd", + "metadata": {}, + "outputs": [], + "source": [ + "# Speicify datastream_dir following the path conventions and check its existence\n", + "DATASTREAM_NAME = \"gvr\"\n", + "DATA_LEVEL = \"c1\"\n", + "site = \"nsa\"\n", + "facility = \"C1\"\n", + "datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )\n", + "print(datastream_dir)\n", + "print(os.path.exists(datastream_dir))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0742f7c1", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: list 5 (random) files under datastream_dir\n", + "files = os.listdir(datastream_dir)\n", + "files[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39b98a36", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: get most recent file\n", + "list_of_files = glob.glob(f\"{datastream_dir}/*\") # * means all if need specific format then *.csv\n", + "latest_file = max(list_of_files, key=os.path.getctime)\n", + "latest_file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "902d514e", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus sort datastream files based on datetime\n", + "files = os.listdir(datastream_dir)\n", + "file_sorted = files.copy()\n", + "file_sorted.sort() \n", + "print(file_sorted[:5])\n", + "\n", + "# to reverse\n", + "file_sorted_reverse = files.copy() \n", + "file_sorted_reverse.sort(reverse=True)\n", + "print(file_sorted_reverse[:5])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec5923b2", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: pattern matching\n", + "# filter the 200709** files under datastream_dir\n", + "files_filter = glob.glob(f'{datastream_dir}/*.200709*.*')\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c4a4aa18", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: use armnotebook_utils.file_filter (TODO) to filter files based on datastream info \n" + ] + }, + { + "cell_type": "markdown", + "id": "cfeb9efc", + "metadata": {}, + "source": [ + "### Load data\n", + "The arm data is stored in [NetCDF](https://en.wikipedia.org/wiki/NetCDF#:~:text=%22NetCDF%20(network%20Common%20Data%20Form,format%20for%20representing%20scientific%20data.) format. We can use xarray's `open_dataset` method to load single file, or `open_mfdataset` to open multiple files. (Note: the latter will still return a single xarray dataset object by combining multiple files.)\n", + "\n", + "Note: open_dataset keeps the file handle open and lazy loads its contents. All parameters are passed directly to open_dataset. It is a preferable method over load_dataset for memory-effiency. (more details, please see [xarray.open_dataset](https://docs.xarray.dev/en/stable/generated/xarray.open_dataset.html))\n", + "\n", + "See the following example in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a440a329", + "metadata": {}, + "outputs": [], + "source": [ + "# open single file\n", + "full_path = latest_file\n", + "print(full_path)\n", + "ds_single = xr.open_dataset(full_path)\n", + "ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0143a3d", + "metadata": {}, + "outputs": [], + "source": [ + "print(ds_single)\n", + "print(type(ds_single))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c0f8939", + "metadata": {}, + "outputs": [], + "source": [ + "# open multiple files\n", + "n_files = 3\n", + "full_paths = [os.path.join(datastream_dir, f_path) for f_path in file_sorted_reverse[:n_files]]\n", + "print(full_paths)\n", + "try: # Note: sometimes multiple files cannot be merged, so we used try except here.\n", + " ds_mutiple = xr.open_mfdataset(full_paths)\n", + " # ds_mutiple\n", + " print(type(ds_mutiple))\n", + " print(ds_mutiple)\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "3fa59943", + "metadata": {}, + "source": [ + "## Explore the data" + ] + }, + { + "cell_type": "markdown", + "id": "571f69b9", + "metadata": {}, + "source": [ + "### NetCDF Data structure \n", + "\n", + "\n", + "(If you are confident with NetCDF basics and xarray essentials, feel free to skip this session.)\n", + "Before we dive into data exploration, there are some eseential concpets we should be familiar with (shown below): \n", + "* Dataset\n", + "* Data array\n", + "* Variable\n", + "* Dimenssion\n", + "* Coordinate\n", + "* Data Type\n", + "* Meta Data (Attributes)\n", + "\n", + "We will not go into details about NetCDF basics and here are some references you might find helpful\n", + "* [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#)\n", + "* [Network Common Data Form (NetCDF)](https://www.unidata.ucar.edu/software/netcdf/) and [A Brief History of (netCDF) Time](https://www.unidata.ucar.edu/software/netcdf/time/recs.html)\n", + "\n", + "\n", + "\n", + "### Xarray essentials\n", + "Earlier we introduced the xarray pacakge and used `open_dataset` and `open_mfdataset` to retrieve NetCDF data file as an xarray Dataset object. Recall that you can review the data in a notebook by using `print(ds)` or `ds`. \n", + "\n", + "Using xarray to retrieve the aforementioned NetCDF basics is straightforwared. In general,\n", + "* Dataset: `ds`\n", + "* Data array: `ds.variables`\n", + "* Variable: `ds.ds.variables`\n", + "* Dimenssion: `ds.dims`\n", + "* Coordinate: `ds.coords`\n", + "* Data Type: `type`\n", + "* Meta Data (Attributes)\n", + "\n", + "Also, here are some references if you are new to xarray\n", + "* [Xarray in 45 minutes](https://tutorial.xarray.dev/overview/xarray-in-45-min.html)\n", + "* [Handling NetCDF Files using XArray for Absolute Beginners](https://towardsdatascience.com/handling-netcdf-files-using-xarray-for-absolute-beginners-111a8ab4463f)\n", + "\n", + "Try the following commands in action\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b3eac323", + "metadata": {}, + "outputs": [], + "source": [ + "ds = ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77ecf85d", + "metadata": {}, + "outputs": [], + "source": [ + "# Dataset \n", + "ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25e7de09", + "metadata": {}, + "outputs": [], + "source": [ + "# Data array, Variable\n", + "# Note: the info can be overwhelming. Do not attempt to grasp everything at the first glance.\n", + "ds.variables" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c41b67e", + "metadata": {}, + "outputs": [], + "source": [ + "# dimenssions\n", + "ds.dims" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "156f1dfc", + "metadata": {}, + "outputs": [], + "source": [ + "# coordinates\n", + "ds.coords" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "277d6064", + "metadata": {}, + "outputs": [], + "source": [ + "# Meta Data (Attributes)\n", + "ds.attrs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d334681f", + "metadata": {}, + "outputs": [], + "source": [ + "# type\n", + "print(type(ds))\n", + "print(type(ds.variables))\n", + "print(type(ds.dims))\n", + "print(type(ds.coords))\n", + "print(type(ds.attrs))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "643399d6", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: retrieve variable names only\n", + "list(ds.variables)" + ] + }, + { + "cell_type": "markdown", + "id": "1cf36878", + "metadata": {}, + "source": [ + "#### Discussion: variable vs. coordinates vs. dimenssions. \n", + "From [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#):\n", + "\n", + "* Dimession: A dimension may be used to represent a real physical dimension, for example, time, latitude, longitude, or height. A dimension might also be used to index other quantities, for example station or model-run-number.\n", + "A netCDF dimension has both a name and a length. A dimension length is an arbitrary positive integer, except that one dimension in a netCDF dataset can have the length UNLIMITED.\n", + "\n", + "* Variables: Variables are used to store the bulk of the data in a netCDF dataset. A variable represents an array of values of the same type. A scalar value is treated as a 0-dimensional array. A variable has a name, a data type, and a shape described by its list of dimensions specified when the variable is created. A variable may also have associated attributes, which may be added, deleted or changed after the variable is created.\n", + "\n", + "* Coordinate (Variables): It is legal for a variable to have the same name as a dimension. Such variables have no special meaning to the netCDF library. However there is a convention that such variables should be treated in a special way by software using this library.\n", + "A variable with the same name as a dimension is called a coordinate variable. It typically defines a physical coordinate corresponding to that dimension.\n", + "\n", + "\n", + "Tips: By definition, coordinates and dimenssions are also variable. For example ds.time is a coordinate but it is also a special variable. But in practice, when we talk about variable, it implies regular/non-coordinate variable. We will use this convention for the remaining notebook. " + ] + }, + { + "cell_type": "markdown", + "id": "d47120f9", + "metadata": {}, + "source": [ + "### Xarray Variable\n", + "\n", + "\n", + "We can use `ds.variables` to access varialbes of a dataset. But many times viewing print-out the whole datasets can be overwhemling. Instead, we would work on the individual variable (also called Xarray data array.) using the `ds[var_name]` syntax, where `var_name` is the name of the variable. See the following examples in action.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5a57e136", + "metadata": {}, + "outputs": [], + "source": [ + "print(type(ds[\"time\"]))\n", + "ds[\"time\"]" + ] + }, + { + "cell_type": "markdown", + "id": "51ec643e", + "metadata": {}, + "source": [ + "#### Variable properties\n", + "For individual varible, the following properties are mostly used with the assocaited xarray method to retrieve them. (assuming var = ds[var_name])\n", + "* name: `var.name`\n", + "* data content: `var.data`\n", + "* attributes: `var.attrs`\n", + "* dimenstions: `var.dims`\n", + "* data type: `var.data.dtype`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a632a525", + "metadata": {}, + "outputs": [], + "source": [ + "var_name = \"time\"\n", + "var = ds[var_name]\n", + "\n", + "print(\"var.name: \\n\", var.name, \"\\n\")\n", + "print(\"var.data: \\n\", var.data, \"\\n\")\n", + "print(\"var.attrs: \\n\", var.attrs, \"\\n\")\n", + "print(\"var.dims: \\n\", var.dims, \"\\n\")\n", + "print(\"var.data.dtype: \\n\", var.data.dtype, \"\\n\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b256b07f", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: indivdual variable info table (collect and display variable info as a tabular format)\n", + "df_info = pd.DataFrame()\n", + "df_info[\"var_name\"] = list(ds.variables)\n", + "df_info[\"dims\"] = df_info.var_name.apply(lambda x: ds[x].dims)\n", + "df_info[\"is_dim\"] = df_info.var_name.apply(lambda x: x in ds.dims)\n", + "df_info[\"n_dim\"] = df_info.var_name.apply(lambda x: len(ds[x].dims))\n", + "df_info[\"attrs\"] = df_info.var_name.apply(lambda x: ds[x].attrs)\n", + "df_info[\"dtype\"] = df_info.var_name.apply(lambda x: ds[x].data.dtype)\n", + "df_info" + ] + }, + { + "cell_type": "markdown", + "id": "e6a36b96", + "metadata": {}, + "source": [ + "### Data cleaning/Preprocessing (skipped)\n", + "Data cleaning and preprocessing is an important stage in the the data analysis pipeline. However it will be out of the scope of this notebook. For more information about data cleaning and preprocessing basics, here are some references. \n", + "* [Xarray Fundamentals](https://earth-env-data-science.github.io/lectures/xarray/xarray.html)\n", + "* [Xarray Tutorial — Pangeo Gallery documentation](http://gallery.pangeo.io/repos/pangeo-data/pangeo-tutorial-gallery/xarray.html)\n", + "* [Pythonic Data Cleaning With pandas and NumPy](https://realpython.com/python-data-cleaning-numpy-pandas/)\n", + "* [Pandas - Cleaning Data](https://www.w3schools.com/python/pandas/pandas_cleaning.asp)" + ] + }, + { + "cell_type": "markdown", + "id": "ae6e1a69", + "metadata": {}, + "source": [ + "### Plotting\n", + "Data visualization (or plotting) is another important data analysis topic and deserves its own discussion. Here in this notebook we will only demonstrate basic tool for simple data visualization tasks.\n", + "\n", + "Here are some reference you might find useful:\n", + "* xarray plotting: https://docs.xarray.dev/en/stable/user-guide/plotting.html\n", + "* Atmospheric Community Toolkit (ACT): https://arm-doe.github.io/ACT/index.html\n", + "\n", + "Note: this notebook is auto-generated using a template. It uses a general idea to select variable(s) to plot and is not customized to each indivdual notebook. Feel free to change the variables in intrrests, especially certain figure is failed to plot." + ] + }, + { + "cell_type": "markdown", + "id": "7e417fb2", + "metadata": {}, + "source": [ + "#### 1-dimenssional basic time series plot\n", + "\n", + "For the following plot we would like to find variables such that\n", + "* it has one and only one dimession\n", + "* \"time\" is its coordinate variable\n", + "* it is not a dimenssion itself,\n", + "* it is not a special variable with substrings within [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "\n", + "Please see the following example in action. (For more details about pandas filtering, please see the following references.)\n", + "* [pandas: multiple conditions while indexing data frame](https://stackoverflow.com/questions/22591174/pandas-multiple-conditions-while-indexing-data-frame-unexpected-behavior)\n", + "* [pandas.DataFrame.apply](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.apply.html)\n", + "* [How to test if a string contains one of the substrings in a list...](https://stackoverflow.com/questions/26577516/how-to-test-if-a-string-contains-one-of-the-substrings-in-a-list-in-pandas" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30edac2d", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==1, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter = df_info[(df_info.n_dim == 1) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4777a659", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_1d = df_filter.var_name.values[0]\n", + " var_1d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb5ae985", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " ds[var_1d].plot()\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "9e41066e", + "metadata": {}, + "source": [ + "#### 2-dimenssional basic plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09bd4adc", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==2, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_2 = df_info[(df_info.n_dim == 2) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2cdea1a9", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_2d = df_filter_2.var_name.values[0]\n", + " var_2d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5f48879", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " print(ds[var_2d].dims)\n", + " # ds[var_2d].plot()\n", + "\n", + " # conventionally, use \"time\" as x-axis\n", + " ds[var_2d].plot(x=\"time\")\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "7554b2a6", + "metadata": {}, + "source": [ + "#### qc-plotting (optional)\n", + "\n", + "Note: act qc plotting has more strict requirement, one of them is the associated qc variable need to have \"flag_masks\" attributes. Which is added by using ds = act.io.armfiles.read_netcdf(files_list), then ds.clean.cleanup()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71c2096f", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " ds_act = act.io.armfiles.read_netcdf(full_path)\n", + " print(type(ds_act))\n", + " ds_act.clean.cleanup()\n", + "\n", + " # or \n", + " # ds.clean.cleanup()\n", + "except Exception as e:\n", + " print(\"ERROR\", e)\n", + " ds_act = ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "844f8505", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter valid variables for ACT qc plotting\n", + "condition = (\"qc_\" + df_info.var_name).apply(lambda x: ds_act[x].attrs.get(\"flag_masks\") is not None \n", + " if x in list(ds.variables) else False)\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_3 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings))) &\n", + " condition\n", + " ]\n", + "df_filter_3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd38cc6", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "try:\n", + " qc_variable = df_filter_3.var_name.values[0]\n", + " print(qc_variable)\n", + "except Exception as e:\n", + " print(e)\n", + " \n", + "try:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + " qc_ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "d76e4d27", + "metadata": {}, + "source": [ + "#### bonus: choose variables to plot from a dropdown menu " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb733804", + "metadata": {}, + "outputs": [], + "source": [ + "# Valid variables filtering\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_4 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82d5c20d", + "metadata": {}, + "outputs": [], + "source": [ + "# example 1: using xarray plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2fc9aaa4", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# fig, ax = plt.subplots(figsize=(10, 4))\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "# fig.clear() # Remove old lines from plot and plot new one\n", + "# if len(ds[var].dims)==2:\n", + "# ds[var].plot(x=\"time\", add_colorbar=False)\n", + "# else:\n", + "# ds[var].plot()\n", + "# plt.grid()\n", + "# plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fabdd802", + "metadata": {}, + "outputs": [], + "source": [ + "# example 2: using act plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "962b4186", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "\n", + "\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "\n", + "# i_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + "# i_display.add_subplots((1,), figsize=(10, 4))\n", + "# ax = i_display.plot(var, subplot_index=(0,), set_title=f\"{var} ({ds_act[var].attrs['long_name']})\",)\n", + "\n", + "# ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "# ax.grid()\n", + "# plt.show()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/GVR/gvr.c1.ipynb b/VAPs/quicklook/GVR/gvr.c1.ipynb new file mode 100644 index 00000000..57a84088 --- /dev/null +++ b/VAPs/quicklook/GVR/gvr.c1.ipynb @@ -0,0 +1,339 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# GVR.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/gvr) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'gvr'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2023-12-16', 'facility': 'C1', 'site': 'nsa', 'start_date': '2006-09-28'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'nsa', 'C1' )\n", + "\n", + "date_start = '2023-12-14'\n", + "date_end = '2023-12-16'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['tbsky1', 'tbsky3', 'tbsky7']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'tbsky1'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'tbsky1'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/HVPS-AIR/HVPS-AIR_tutorial.ipynb b/VAPs/quicklook/HVPS-AIR/HVPS-AIR_tutorial.ipynb new file mode 100644 index 00000000..2cf77f39 --- /dev/null +++ b/VAPs/quicklook/HVPS-AIR/HVPS-AIR_tutorial.ipynb @@ -0,0 +1,838 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# AAFHVPS.C1 Notebook\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/hvps-air) for more information about this vap." + ] + }, + { + "cell_type": "markdown", + "id": "97097763", + "metadata": {}, + "source": [ + "In this notebook, we demonstrate the workflow to explore ARM vap data (using aafhvps as an example.) Value-added products (VAPs) are higher-order data products that have been analyzed and processed to ease scientists’ use of ARM data in atmospheric research and global climate models. \n", + "Here is the main content we will cover." + ] + }, + { + "cell_type": "markdown", + "id": "eddec40f", + "metadata": {}, + "source": [ + "# Table of Content\n", + "## Access the data\n", + "* How to retrieve the data\n", + "* Data path and file name conventions\n", + "* Load data\n", + "## Explore the data\n", + "* NetCDF Data structure\n", + "* Xarray essentials\n", + "* Xarray Variable\n", + "## Plot the data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "import random\n", + "\n", + "import glob\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "2372d5be", + "metadata": {}, + "source": [ + "## Access the data" + ] + }, + { + "cell_type": "markdown", + "id": "5b0d9684", + "metadata": {}, + "source": [ + "### How to retrieve the data\n", + "We assume the path \"/data/archive\" is available where you are running this notebook. You can use `os.path.exists(\"/data/archive\")` to verify if the path exists at your machine.\n", + "\n", + "\n", + "### Data path and file name conventions\n", + "There several common terminologies regarding ARM data, for example, data-stream-name, data-level, site, facility, instrument, etc. (For more details, please see [ARM Data File Standards Version 1.2](https://www.google.com/search?q=arm+datastream+facility+cite+definition&rlz=1C1GCEJ_enUS1029US1029&ei=hb41ZICrBPukqtsPvMODIA&ved=0ahUKEwjAgeiS0aL-AhV7kmoFHbzhAAQQ4dUDCBA&uact=5&oq=arm+datastream+facility+cite+definition&gs_lcp=Cgxnd3Mtd2l6LXNlcnAQAzIFCAAQogQyBQgAEKIEMgUIABCiBDIFCAAQogQyBQgAEKIEOgoIABBHENYEELADOgoIIRCgARDDBBAKSgQIQRgAUJ4DWIoOYO0PaAFwAXgAgAGMAYgBuQmSAQMyLjmYAQCgAQHIAQjAAQE&sclient=gws-wiz-serp).) \n", + "\n", + "For example, this notebook is called `aafhvps.c1`, where `aafhvps` is the \"datastream name\", and the `{process.ds_class_level}}` is the \"data level\".\n", + "\n", + "This datastream also contains site `cor` and facility `F1`. (Note: individual datastream might have multiple site-facility pairs.)\n", + "In such a case, the data of this data-stream is stored at `/data/archive/cor/coraafhvpsF1.c1`, which is in the format of `//.`. We can use the following method to assign the data-stream directory `datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )`\n", + "\n", + "The data files under datastream_dir also follows naming conventions. But once reach the datastream_dir level, the most import file naming convention to differentiate the files is \"yyyyMMdd.hhmmss\", which comes handy to filter out files based on datetime. For example, we can use `glob.glob(f'{datastream_dir}/*.200709*.*')` to filter files in 2007 September.\n", + "\n", + "(Note: refer to the https://adc.arm.gov/discovery/#/ and https://adc.arm.gov/solr8/metadata/select API to explore ARM datastream and assoicated available sites and facilities\n", + "\n", + "Please see the following examples in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d7e9eb85", + "metadata": {}, + "outputs": [], + "source": [ + "# Verify if DATA_DIR path exists\n", + "DATA_DIR = \"/data/archive\"\n", + "os.path.exists(DATA_DIR)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "586993fd", + "metadata": {}, + "outputs": [], + "source": [ + "# Speicify datastream_dir following the path conventions and check its existence\n", + "DATASTREAM_NAME = \"aafhvps\"\n", + "DATA_LEVEL = \"c1\"\n", + "site = \"cor\"\n", + "facility = \"F1\"\n", + "datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )\n", + "print(datastream_dir)\n", + "print(os.path.exists(datastream_dir))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0742f7c1", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: list 5 (random) files under datastream_dir\n", + "files = os.listdir(datastream_dir)\n", + "files[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39b98a36", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: get most recent file\n", + "list_of_files = glob.glob(f\"{datastream_dir}/*\") # * means all if need specific format then *.csv\n", + "latest_file = max(list_of_files, key=os.path.getctime)\n", + "latest_file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "902d514e", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus sort datastream files based on datetime\n", + "files = os.listdir(datastream_dir)\n", + "file_sorted = files.copy()\n", + "file_sorted.sort() \n", + "print(file_sorted[:5])\n", + "\n", + "# to reverse\n", + "file_sorted_reverse = files.copy() \n", + "file_sorted_reverse.sort(reverse=True)\n", + "print(file_sorted_reverse[:5])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec5923b2", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: pattern matching\n", + "# filter the 200709** files under datastream_dir\n", + "files_filter = glob.glob(f'{datastream_dir}/*.200709*.*')\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c4a4aa18", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: use armnotebook_utils.file_filter (TODO) to filter files based on datastream info \n" + ] + }, + { + "cell_type": "markdown", + "id": "cfeb9efc", + "metadata": {}, + "source": [ + "### Load data\n", + "The arm data is stored in [NetCDF](https://en.wikipedia.org/wiki/NetCDF#:~:text=%22NetCDF%20(network%20Common%20Data%20Form,format%20for%20representing%20scientific%20data.) format. We can use xarray's `open_dataset` method to load single file, or `open_mfdataset` to open multiple files. (Note: the latter will still return a single xarray dataset object by combining multiple files.)\n", + "\n", + "Note: open_dataset keeps the file handle open and lazy loads its contents. All parameters are passed directly to open_dataset. It is a preferable method over load_dataset for memory-effiency. (more details, please see [xarray.open_dataset](https://docs.xarray.dev/en/stable/generated/xarray.open_dataset.html))\n", + "\n", + "See the following example in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a440a329", + "metadata": {}, + "outputs": [], + "source": [ + "# open single file\n", + "full_path = latest_file\n", + "print(full_path)\n", + "ds_single = xr.open_dataset(full_path)\n", + "ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0143a3d", + "metadata": {}, + "outputs": [], + "source": [ + "print(ds_single)\n", + "print(type(ds_single))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c0f8939", + "metadata": {}, + "outputs": [], + "source": [ + "# open multiple files\n", + "n_files = 3\n", + "full_paths = [os.path.join(datastream_dir, f_path) for f_path in file_sorted_reverse[:n_files]]\n", + "print(full_paths)\n", + "try: # Note: sometimes multiple files cannot be merged, so we used try except here.\n", + " ds_mutiple = xr.open_mfdataset(full_paths)\n", + " # ds_mutiple\n", + " print(type(ds_mutiple))\n", + " print(ds_mutiple)\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "3fa59943", + "metadata": {}, + "source": [ + "## Explore the data" + ] + }, + { + "cell_type": "markdown", + "id": "571f69b9", + "metadata": {}, + "source": [ + "### NetCDF Data structure \n", + "\n", + "\n", + "(If you are confident with NetCDF basics and xarray essentials, feel free to skip this session.)\n", + "Before we dive into data exploration, there are some eseential concpets we should be familiar with (shown below): \n", + "* Dataset\n", + "* Data array\n", + "* Variable\n", + "* Dimenssion\n", + "* Coordinate\n", + "* Data Type\n", + "* Meta Data (Attributes)\n", + "\n", + "We will not go into details about NetCDF basics and here are some references you might find helpful\n", + "* [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#)\n", + "* [Network Common Data Form (NetCDF)](https://www.unidata.ucar.edu/software/netcdf/) and [A Brief History of (netCDF) Time](https://www.unidata.ucar.edu/software/netcdf/time/recs.html)\n", + "\n", + "\n", + "\n", + "### Xarray essentials\n", + "Earlier we introduced the xarray pacakge and used `open_dataset` and `open_mfdataset` to retrieve NetCDF data file as an xarray Dataset object. Recall that you can review the data in a notebook by using `print(ds)` or `ds`. \n", + "\n", + "Using xarray to retrieve the aforementioned NetCDF basics is straightforwared. In general,\n", + "* Dataset: `ds`\n", + "* Data array: `ds.variables`\n", + "* Variable: `ds.ds.variables`\n", + "* Dimenssion: `ds.dims`\n", + "* Coordinate: `ds.coords`\n", + "* Data Type: `type`\n", + "* Meta Data (Attributes)\n", + "\n", + "Also, here are some references if you are new to xarray\n", + "* [Xarray in 45 minutes](https://tutorial.xarray.dev/overview/xarray-in-45-min.html)\n", + "* [Handling NetCDF Files using XArray for Absolute Beginners](https://towardsdatascience.com/handling-netcdf-files-using-xarray-for-absolute-beginners-111a8ab4463f)\n", + "\n", + "Try the following commands in action\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b3eac323", + "metadata": {}, + "outputs": [], + "source": [ + "ds = ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77ecf85d", + "metadata": {}, + "outputs": [], + "source": [ + "# Dataset \n", + "ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25e7de09", + "metadata": {}, + "outputs": [], + "source": [ + "# Data array, Variable\n", + "# Note: the info can be overwhelming. Do not attempt to grasp everything at the first glance.\n", + "ds.variables" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c41b67e", + "metadata": {}, + "outputs": [], + "source": [ + "# dimenssions\n", + "ds.dims" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "156f1dfc", + "metadata": {}, + "outputs": [], + "source": [ + "# coordinates\n", + "ds.coords" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "277d6064", + "metadata": {}, + "outputs": [], + "source": [ + "# Meta Data (Attributes)\n", + "ds.attrs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d334681f", + "metadata": {}, + "outputs": [], + "source": [ + "# type\n", + "print(type(ds))\n", + "print(type(ds.variables))\n", + "print(type(ds.dims))\n", + "print(type(ds.coords))\n", + "print(type(ds.attrs))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "643399d6", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: retrieve variable names only\n", + "list(ds.variables)" + ] + }, + { + "cell_type": "markdown", + "id": "1cf36878", + "metadata": {}, + "source": [ + "#### Discussion: variable vs. coordinates vs. dimenssions. \n", + "From [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#):\n", + "\n", + "* Dimession: A dimension may be used to represent a real physical dimension, for example, time, latitude, longitude, or height. A dimension might also be used to index other quantities, for example station or model-run-number.\n", + "A netCDF dimension has both a name and a length. A dimension length is an arbitrary positive integer, except that one dimension in a netCDF dataset can have the length UNLIMITED.\n", + "\n", + "* Variables: Variables are used to store the bulk of the data in a netCDF dataset. A variable represents an array of values of the same type. A scalar value is treated as a 0-dimensional array. A variable has a name, a data type, and a shape described by its list of dimensions specified when the variable is created. A variable may also have associated attributes, which may be added, deleted or changed after the variable is created.\n", + "\n", + "* Coordinate (Variables): It is legal for a variable to have the same name as a dimension. Such variables have no special meaning to the netCDF library. However there is a convention that such variables should be treated in a special way by software using this library.\n", + "A variable with the same name as a dimension is called a coordinate variable. It typically defines a physical coordinate corresponding to that dimension.\n", + "\n", + "\n", + "Tips: By definition, coordinates and dimenssions are also variable. For example ds.time is a coordinate but it is also a special variable. But in practice, when we talk about variable, it implies regular/non-coordinate variable. We will use this convention for the remaining notebook. " + ] + }, + { + "cell_type": "markdown", + "id": "d47120f9", + "metadata": {}, + "source": [ + "### Xarray Variable\n", + "\n", + "\n", + "We can use `ds.variables` to access varialbes of a dataset. But many times viewing print-out the whole datasets can be overwhemling. Instead, we would work on the individual variable (also called Xarray data array.) using the `ds[var_name]` syntax, where `var_name` is the name of the variable. See the following examples in action.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5a57e136", + "metadata": {}, + "outputs": [], + "source": [ + "print(type(ds[\"time\"]))\n", + "ds[\"time\"]" + ] + }, + { + "cell_type": "markdown", + "id": "51ec643e", + "metadata": {}, + "source": [ + "#### Variable properties\n", + "For individual varible, the following properties are mostly used with the assocaited xarray method to retrieve them. (assuming var = ds[var_name])\n", + "* name: `var.name`\n", + "* data content: `var.data`\n", + "* attributes: `var.attrs`\n", + "* dimenstions: `var.dims`\n", + "* data type: `var.data.dtype`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a632a525", + "metadata": {}, + "outputs": [], + "source": [ + "var_name = \"time\"\n", + "var = ds[var_name]\n", + "\n", + "print(\"var.name: \\n\", var.name, \"\\n\")\n", + "print(\"var.data: \\n\", var.data, \"\\n\")\n", + "print(\"var.attrs: \\n\", var.attrs, \"\\n\")\n", + "print(\"var.dims: \\n\", var.dims, \"\\n\")\n", + "print(\"var.data.dtype: \\n\", var.data.dtype, \"\\n\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b256b07f", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: indivdual variable info table (collect and display variable info as a tabular format)\n", + "df_info = pd.DataFrame()\n", + "df_info[\"var_name\"] = list(ds.variables)\n", + "df_info[\"dims\"] = df_info.var_name.apply(lambda x: ds[x].dims)\n", + "df_info[\"is_dim\"] = df_info.var_name.apply(lambda x: x in ds.dims)\n", + "df_info[\"n_dim\"] = df_info.var_name.apply(lambda x: len(ds[x].dims))\n", + "df_info[\"attrs\"] = df_info.var_name.apply(lambda x: ds[x].attrs)\n", + "df_info[\"dtype\"] = df_info.var_name.apply(lambda x: ds[x].data.dtype)\n", + "df_info" + ] + }, + { + "cell_type": "markdown", + "id": "e6a36b96", + "metadata": {}, + "source": [ + "### Data cleaning/Preprocessing (skipped)\n", + "Data cleaning and preprocessing is an important stage in the the data analysis pipeline. However it will be out of the scope of this notebook. For more information about data cleaning and preprocessing basics, here are some references. \n", + "* [Xarray Fundamentals](https://earth-env-data-science.github.io/lectures/xarray/xarray.html)\n", + "* [Xarray Tutorial — Pangeo Gallery documentation](http://gallery.pangeo.io/repos/pangeo-data/pangeo-tutorial-gallery/xarray.html)\n", + "* [Pythonic Data Cleaning With pandas and NumPy](https://realpython.com/python-data-cleaning-numpy-pandas/)\n", + "* [Pandas - Cleaning Data](https://www.w3schools.com/python/pandas/pandas_cleaning.asp)" + ] + }, + { + "cell_type": "markdown", + "id": "ae6e1a69", + "metadata": {}, + "source": [ + "### Plotting\n", + "Data visualization (or plotting) is another important data analysis topic and deserves its own discussion. Here in this notebook we will only demonstrate basic tool for simple data visualization tasks.\n", + "\n", + "Here are some reference you might find useful:\n", + "* xarray plotting: https://docs.xarray.dev/en/stable/user-guide/plotting.html\n", + "* Atmospheric Community Toolkit (ACT): https://arm-doe.github.io/ACT/index.html\n", + "\n", + "Note: this notebook is auto-generated using a template. It uses a general idea to select variable(s) to plot and is not customized to each indivdual notebook. Feel free to change the variables in intrrests, especially certain figure is failed to plot." + ] + }, + { + "cell_type": "markdown", + "id": "7e417fb2", + "metadata": {}, + "source": [ + "#### 1-dimenssional basic time series plot\n", + "\n", + "For the following plot we would like to find variables such that\n", + "* it has one and only one dimession\n", + "* \"time\" is its coordinate variable\n", + "* it is not a dimenssion itself,\n", + "* it is not a special variable with substrings within [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "\n", + "Please see the following example in action. (For more details about pandas filtering, please see the following references.)\n", + "* [pandas: multiple conditions while indexing data frame](https://stackoverflow.com/questions/22591174/pandas-multiple-conditions-while-indexing-data-frame-unexpected-behavior)\n", + "* [pandas.DataFrame.apply](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.apply.html)\n", + "* [How to test if a string contains one of the substrings in a list...](https://stackoverflow.com/questions/26577516/how-to-test-if-a-string-contains-one-of-the-substrings-in-a-list-in-pandas" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30edac2d", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==1, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter = df_info[(df_info.n_dim == 1) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4777a659", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_1d = df_filter.var_name.values[0]\n", + " var_1d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb5ae985", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " ds[var_1d].plot()\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "9e41066e", + "metadata": {}, + "source": [ + "#### 2-dimenssional basic plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09bd4adc", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==2, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_2 = df_info[(df_info.n_dim == 2) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2cdea1a9", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_2d = df_filter_2.var_name.values[0]\n", + " var_2d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5f48879", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " print(ds[var_2d].dims)\n", + " # ds[var_2d].plot()\n", + "\n", + " # conventionally, use \"time\" as x-axis\n", + " ds[var_2d].plot(x=\"time\")\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "7554b2a6", + "metadata": {}, + "source": [ + "#### qc-plotting (optional)\n", + "\n", + "Note: act qc plotting has more strict requirement, one of them is the associated qc variable need to have \"flag_masks\" attributes. Which is added by using ds = act.io.armfiles.read_netcdf(files_list), then ds.clean.cleanup()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71c2096f", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " ds_act = act.io.armfiles.read_netcdf(full_path)\n", + " print(type(ds_act))\n", + " ds_act.clean.cleanup()\n", + "\n", + " # or \n", + " # ds.clean.cleanup()\n", + "except Exception as e:\n", + " print(\"ERROR\", e)\n", + " ds_act = ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "844f8505", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter valid variables for ACT qc plotting\n", + "condition = (\"qc_\" + df_info.var_name).apply(lambda x: ds_act[x].attrs.get(\"flag_masks\") is not None \n", + " if x in list(ds.variables) else False)\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_3 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings))) &\n", + " condition\n", + " ]\n", + "df_filter_3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd38cc6", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "try:\n", + " qc_variable = df_filter_3.var_name.values[0]\n", + " print(qc_variable)\n", + "except Exception as e:\n", + " print(e)\n", + " \n", + "try:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + " qc_ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "d76e4d27", + "metadata": {}, + "source": [ + "#### bonus: choose variables to plot from a dropdown menu " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb733804", + "metadata": {}, + "outputs": [], + "source": [ + "# Valid variables filtering\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_4 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82d5c20d", + "metadata": {}, + "outputs": [], + "source": [ + "# example 1: using xarray plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2fc9aaa4", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# fig, ax = plt.subplots(figsize=(10, 4))\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "# fig.clear() # Remove old lines from plot and plot new one\n", + "# if len(ds[var].dims)==2:\n", + "# ds[var].plot(x=\"time\", add_colorbar=False)\n", + "# else:\n", + "# ds[var].plot()\n", + "# plt.grid()\n", + "# plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fabdd802", + "metadata": {}, + "outputs": [], + "source": [ + "# example 2: using act plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "962b4186", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "\n", + "\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "\n", + "# i_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + "# i_display.add_subplots((1,), figsize=(10, 4))\n", + "# ax = i_display.plot(var, subplot_index=(0,), set_title=f\"{var} ({ds_act[var].attrs['long_name']})\",)\n", + "\n", + "# ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "# ax.grid()\n", + "# plt.show()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/HVPS-AIR/aafhvps.c1.ipynb b/VAPs/quicklook/HVPS-AIR/aafhvps.c1.ipynb new file mode 100644 index 00000000..28960894 --- /dev/null +++ b/VAPs/quicklook/HVPS-AIR/aafhvps.c1.ipynb @@ -0,0 +1,297 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# AAFHVPS.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/hvps-air) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'aafhvps'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2018-12-08', 'facility': 'F1', 'site': 'cor', 'start_date': '2018-11-04'}, {'end_date': '2018-02-19', 'facility': 'F1', 'site': 'ena', 'start_date': '2017-06-21'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'cor', 'F1' )\n", + "\n", + "date_start = '2018-12-06'\n", + "date_end = '2018-12-08'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['total_number_concentration', 'number_concentration']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'total_number_concentration'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/INLETCVI-AIR/INLETCVI-AIR_tutorial.ipynb b/VAPs/quicklook/INLETCVI-AIR/INLETCVI-AIR_tutorial.ipynb new file mode 100644 index 00000000..d345342a --- /dev/null +++ b/VAPs/quicklook/INLETCVI-AIR/INLETCVI-AIR_tutorial.ipynb @@ -0,0 +1,838 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# AAFINLETCVI.C1 Notebook\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/inletcvi-air) for more information about this vap." + ] + }, + { + "cell_type": "markdown", + "id": "97097763", + "metadata": {}, + "source": [ + "In this notebook, we demonstrate the workflow to explore ARM vap data (using aafinletcvi as an example.) Value-added products (VAPs) are higher-order data products that have been analyzed and processed to ease scientists’ use of ARM data in atmospheric research and global climate models. \n", + "Here is the main content we will cover." + ] + }, + { + "cell_type": "markdown", + "id": "eddec40f", + "metadata": {}, + "source": [ + "# Table of Content\n", + "## Access the data\n", + "* How to retrieve the data\n", + "* Data path and file name conventions\n", + "* Load data\n", + "## Explore the data\n", + "* NetCDF Data structure\n", + "* Xarray essentials\n", + "* Xarray Variable\n", + "## Plot the data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "import random\n", + "\n", + "import glob\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "2372d5be", + "metadata": {}, + "source": [ + "## Access the data" + ] + }, + { + "cell_type": "markdown", + "id": "5b0d9684", + "metadata": {}, + "source": [ + "### How to retrieve the data\n", + "We assume the path \"/data/archive\" is available where you are running this notebook. You can use `os.path.exists(\"/data/archive\")` to verify if the path exists at your machine.\n", + "\n", + "\n", + "### Data path and file name conventions\n", + "There several common terminologies regarding ARM data, for example, data-stream-name, data-level, site, facility, instrument, etc. (For more details, please see [ARM Data File Standards Version 1.2](https://www.google.com/search?q=arm+datastream+facility+cite+definition&rlz=1C1GCEJ_enUS1029US1029&ei=hb41ZICrBPukqtsPvMODIA&ved=0ahUKEwjAgeiS0aL-AhV7kmoFHbzhAAQQ4dUDCBA&uact=5&oq=arm+datastream+facility+cite+definition&gs_lcp=Cgxnd3Mtd2l6LXNlcnAQAzIFCAAQogQyBQgAEKIEMgUIABCiBDIFCAAQogQyBQgAEKIEOgoIABBHENYEELADOgoIIRCgARDDBBAKSgQIQRgAUJ4DWIoOYO0PaAFwAXgAgAGMAYgBuQmSAQMyLjmYAQCgAQHIAQjAAQE&sclient=gws-wiz-serp).) \n", + "\n", + "For example, this notebook is called `aafinletcvi.c1`, where `aafinletcvi` is the \"datastream name\", and the `{process.ds_class_level}}` is the \"data level\".\n", + "\n", + "This datastream also contains site `cor` and facility `F1`. (Note: individual datastream might have multiple site-facility pairs.)\n", + "In such a case, the data of this data-stream is stored at `/data/archive/cor/coraafinletcviF1.c1`, which is in the format of `//.`. We can use the following method to assign the data-stream directory `datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )`\n", + "\n", + "The data files under datastream_dir also follows naming conventions. But once reach the datastream_dir level, the most import file naming convention to differentiate the files is \"yyyyMMdd.hhmmss\", which comes handy to filter out files based on datetime. For example, we can use `glob.glob(f'{datastream_dir}/*.200709*.*')` to filter files in 2007 September.\n", + "\n", + "(Note: refer to the https://adc.arm.gov/discovery/#/ and https://adc.arm.gov/solr8/metadata/select API to explore ARM datastream and assoicated available sites and facilities\n", + "\n", + "Please see the following examples in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d7e9eb85", + "metadata": {}, + "outputs": [], + "source": [ + "# Verify if DATA_DIR path exists\n", + "DATA_DIR = \"/data/archive\"\n", + "os.path.exists(DATA_DIR)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "586993fd", + "metadata": {}, + "outputs": [], + "source": [ + "# Speicify datastream_dir following the path conventions and check its existence\n", + "DATASTREAM_NAME = \"aafinletcvi\"\n", + "DATA_LEVEL = \"c1\"\n", + "site = \"cor\"\n", + "facility = \"F1\"\n", + "datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )\n", + "print(datastream_dir)\n", + "print(os.path.exists(datastream_dir))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0742f7c1", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: list 5 (random) files under datastream_dir\n", + "files = os.listdir(datastream_dir)\n", + "files[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39b98a36", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: get most recent file\n", + "list_of_files = glob.glob(f\"{datastream_dir}/*\") # * means all if need specific format then *.csv\n", + "latest_file = max(list_of_files, key=os.path.getctime)\n", + "latest_file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "902d514e", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus sort datastream files based on datetime\n", + "files = os.listdir(datastream_dir)\n", + "file_sorted = files.copy()\n", + "file_sorted.sort() \n", + "print(file_sorted[:5])\n", + "\n", + "# to reverse\n", + "file_sorted_reverse = files.copy() \n", + "file_sorted_reverse.sort(reverse=True)\n", + "print(file_sorted_reverse[:5])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec5923b2", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: pattern matching\n", + "# filter the 200709** files under datastream_dir\n", + "files_filter = glob.glob(f'{datastream_dir}/*.200709*.*')\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c4a4aa18", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: use armnotebook_utils.file_filter (TODO) to filter files based on datastream info \n" + ] + }, + { + "cell_type": "markdown", + "id": "cfeb9efc", + "metadata": {}, + "source": [ + "### Load data\n", + "The arm data is stored in [NetCDF](https://en.wikipedia.org/wiki/NetCDF#:~:text=%22NetCDF%20(network%20Common%20Data%20Form,format%20for%20representing%20scientific%20data.) format. We can use xarray's `open_dataset` method to load single file, or `open_mfdataset` to open multiple files. (Note: the latter will still return a single xarray dataset object by combining multiple files.)\n", + "\n", + "Note: open_dataset keeps the file handle open and lazy loads its contents. All parameters are passed directly to open_dataset. It is a preferable method over load_dataset for memory-effiency. (more details, please see [xarray.open_dataset](https://docs.xarray.dev/en/stable/generated/xarray.open_dataset.html))\n", + "\n", + "See the following example in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a440a329", + "metadata": {}, + "outputs": [], + "source": [ + "# open single file\n", + "full_path = latest_file\n", + "print(full_path)\n", + "ds_single = xr.open_dataset(full_path)\n", + "ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0143a3d", + "metadata": {}, + "outputs": [], + "source": [ + "print(ds_single)\n", + "print(type(ds_single))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c0f8939", + "metadata": {}, + "outputs": [], + "source": [ + "# open multiple files\n", + "n_files = 3\n", + "full_paths = [os.path.join(datastream_dir, f_path) for f_path in file_sorted_reverse[:n_files]]\n", + "print(full_paths)\n", + "try: # Note: sometimes multiple files cannot be merged, so we used try except here.\n", + " ds_mutiple = xr.open_mfdataset(full_paths)\n", + " # ds_mutiple\n", + " print(type(ds_mutiple))\n", + " print(ds_mutiple)\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "3fa59943", + "metadata": {}, + "source": [ + "## Explore the data" + ] + }, + { + "cell_type": "markdown", + "id": "571f69b9", + "metadata": {}, + "source": [ + "### NetCDF Data structure \n", + "\n", + "\n", + "(If you are confident with NetCDF basics and xarray essentials, feel free to skip this session.)\n", + "Before we dive into data exploration, there are some eseential concpets we should be familiar with (shown below): \n", + "* Dataset\n", + "* Data array\n", + "* Variable\n", + "* Dimenssion\n", + "* Coordinate\n", + "* Data Type\n", + "* Meta Data (Attributes)\n", + "\n", + "We will not go into details about NetCDF basics and here are some references you might find helpful\n", + "* [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#)\n", + "* [Network Common Data Form (NetCDF)](https://www.unidata.ucar.edu/software/netcdf/) and [A Brief History of (netCDF) Time](https://www.unidata.ucar.edu/software/netcdf/time/recs.html)\n", + "\n", + "\n", + "\n", + "### Xarray essentials\n", + "Earlier we introduced the xarray pacakge and used `open_dataset` and `open_mfdataset` to retrieve NetCDF data file as an xarray Dataset object. Recall that you can review the data in a notebook by using `print(ds)` or `ds`. \n", + "\n", + "Using xarray to retrieve the aforementioned NetCDF basics is straightforwared. In general,\n", + "* Dataset: `ds`\n", + "* Data array: `ds.variables`\n", + "* Variable: `ds.ds.variables`\n", + "* Dimenssion: `ds.dims`\n", + "* Coordinate: `ds.coords`\n", + "* Data Type: `type`\n", + "* Meta Data (Attributes)\n", + "\n", + "Also, here are some references if you are new to xarray\n", + "* [Xarray in 45 minutes](https://tutorial.xarray.dev/overview/xarray-in-45-min.html)\n", + "* [Handling NetCDF Files using XArray for Absolute Beginners](https://towardsdatascience.com/handling-netcdf-files-using-xarray-for-absolute-beginners-111a8ab4463f)\n", + "\n", + "Try the following commands in action\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b3eac323", + "metadata": {}, + "outputs": [], + "source": [ + "ds = ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77ecf85d", + "metadata": {}, + "outputs": [], + "source": [ + "# Dataset \n", + "ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25e7de09", + "metadata": {}, + "outputs": [], + "source": [ + "# Data array, Variable\n", + "# Note: the info can be overwhelming. Do not attempt to grasp everything at the first glance.\n", + "ds.variables" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c41b67e", + "metadata": {}, + "outputs": [], + "source": [ + "# dimenssions\n", + "ds.dims" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "156f1dfc", + "metadata": {}, + "outputs": [], + "source": [ + "# coordinates\n", + "ds.coords" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "277d6064", + "metadata": {}, + "outputs": [], + "source": [ + "# Meta Data (Attributes)\n", + "ds.attrs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d334681f", + "metadata": {}, + "outputs": [], + "source": [ + "# type\n", + "print(type(ds))\n", + "print(type(ds.variables))\n", + "print(type(ds.dims))\n", + "print(type(ds.coords))\n", + "print(type(ds.attrs))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "643399d6", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: retrieve variable names only\n", + "list(ds.variables)" + ] + }, + { + "cell_type": "markdown", + "id": "1cf36878", + "metadata": {}, + "source": [ + "#### Discussion: variable vs. coordinates vs. dimenssions. \n", + "From [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#):\n", + "\n", + "* Dimession: A dimension may be used to represent a real physical dimension, for example, time, latitude, longitude, or height. A dimension might also be used to index other quantities, for example station or model-run-number.\n", + "A netCDF dimension has both a name and a length. A dimension length is an arbitrary positive integer, except that one dimension in a netCDF dataset can have the length UNLIMITED.\n", + "\n", + "* Variables: Variables are used to store the bulk of the data in a netCDF dataset. A variable represents an array of values of the same type. A scalar value is treated as a 0-dimensional array. A variable has a name, a data type, and a shape described by its list of dimensions specified when the variable is created. A variable may also have associated attributes, which may be added, deleted or changed after the variable is created.\n", + "\n", + "* Coordinate (Variables): It is legal for a variable to have the same name as a dimension. Such variables have no special meaning to the netCDF library. However there is a convention that such variables should be treated in a special way by software using this library.\n", + "A variable with the same name as a dimension is called a coordinate variable. It typically defines a physical coordinate corresponding to that dimension.\n", + "\n", + "\n", + "Tips: By definition, coordinates and dimenssions are also variable. For example ds.time is a coordinate but it is also a special variable. But in practice, when we talk about variable, it implies regular/non-coordinate variable. We will use this convention for the remaining notebook. " + ] + }, + { + "cell_type": "markdown", + "id": "d47120f9", + "metadata": {}, + "source": [ + "### Xarray Variable\n", + "\n", + "\n", + "We can use `ds.variables` to access varialbes of a dataset. But many times viewing print-out the whole datasets can be overwhemling. Instead, we would work on the individual variable (also called Xarray data array.) using the `ds[var_name]` syntax, where `var_name` is the name of the variable. See the following examples in action.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5a57e136", + "metadata": {}, + "outputs": [], + "source": [ + "print(type(ds[\"time\"]))\n", + "ds[\"time\"]" + ] + }, + { + "cell_type": "markdown", + "id": "51ec643e", + "metadata": {}, + "source": [ + "#### Variable properties\n", + "For individual varible, the following properties are mostly used with the assocaited xarray method to retrieve them. (assuming var = ds[var_name])\n", + "* name: `var.name`\n", + "* data content: `var.data`\n", + "* attributes: `var.attrs`\n", + "* dimenstions: `var.dims`\n", + "* data type: `var.data.dtype`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a632a525", + "metadata": {}, + "outputs": [], + "source": [ + "var_name = \"time\"\n", + "var = ds[var_name]\n", + "\n", + "print(\"var.name: \\n\", var.name, \"\\n\")\n", + "print(\"var.data: \\n\", var.data, \"\\n\")\n", + "print(\"var.attrs: \\n\", var.attrs, \"\\n\")\n", + "print(\"var.dims: \\n\", var.dims, \"\\n\")\n", + "print(\"var.data.dtype: \\n\", var.data.dtype, \"\\n\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b256b07f", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: indivdual variable info table (collect and display variable info as a tabular format)\n", + "df_info = pd.DataFrame()\n", + "df_info[\"var_name\"] = list(ds.variables)\n", + "df_info[\"dims\"] = df_info.var_name.apply(lambda x: ds[x].dims)\n", + "df_info[\"is_dim\"] = df_info.var_name.apply(lambda x: x in ds.dims)\n", + "df_info[\"n_dim\"] = df_info.var_name.apply(lambda x: len(ds[x].dims))\n", + "df_info[\"attrs\"] = df_info.var_name.apply(lambda x: ds[x].attrs)\n", + "df_info[\"dtype\"] = df_info.var_name.apply(lambda x: ds[x].data.dtype)\n", + "df_info" + ] + }, + { + "cell_type": "markdown", + "id": "e6a36b96", + "metadata": {}, + "source": [ + "### Data cleaning/Preprocessing (skipped)\n", + "Data cleaning and preprocessing is an important stage in the the data analysis pipeline. However it will be out of the scope of this notebook. For more information about data cleaning and preprocessing basics, here are some references. \n", + "* [Xarray Fundamentals](https://earth-env-data-science.github.io/lectures/xarray/xarray.html)\n", + "* [Xarray Tutorial — Pangeo Gallery documentation](http://gallery.pangeo.io/repos/pangeo-data/pangeo-tutorial-gallery/xarray.html)\n", + "* [Pythonic Data Cleaning With pandas and NumPy](https://realpython.com/python-data-cleaning-numpy-pandas/)\n", + "* [Pandas - Cleaning Data](https://www.w3schools.com/python/pandas/pandas_cleaning.asp)" + ] + }, + { + "cell_type": "markdown", + "id": "ae6e1a69", + "metadata": {}, + "source": [ + "### Plotting\n", + "Data visualization (or plotting) is another important data analysis topic and deserves its own discussion. Here in this notebook we will only demonstrate basic tool for simple data visualization tasks.\n", + "\n", + "Here are some reference you might find useful:\n", + "* xarray plotting: https://docs.xarray.dev/en/stable/user-guide/plotting.html\n", + "* Atmospheric Community Toolkit (ACT): https://arm-doe.github.io/ACT/index.html\n", + "\n", + "Note: this notebook is auto-generated using a template. It uses a general idea to select variable(s) to plot and is not customized to each indivdual notebook. Feel free to change the variables in intrrests, especially certain figure is failed to plot." + ] + }, + { + "cell_type": "markdown", + "id": "7e417fb2", + "metadata": {}, + "source": [ + "#### 1-dimenssional basic time series plot\n", + "\n", + "For the following plot we would like to find variables such that\n", + "* it has one and only one dimession\n", + "* \"time\" is its coordinate variable\n", + "* it is not a dimenssion itself,\n", + "* it is not a special variable with substrings within [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "\n", + "Please see the following example in action. (For more details about pandas filtering, please see the following references.)\n", + "* [pandas: multiple conditions while indexing data frame](https://stackoverflow.com/questions/22591174/pandas-multiple-conditions-while-indexing-data-frame-unexpected-behavior)\n", + "* [pandas.DataFrame.apply](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.apply.html)\n", + "* [How to test if a string contains one of the substrings in a list...](https://stackoverflow.com/questions/26577516/how-to-test-if-a-string-contains-one-of-the-substrings-in-a-list-in-pandas" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30edac2d", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==1, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter = df_info[(df_info.n_dim == 1) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4777a659", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_1d = df_filter.var_name.values[0]\n", + " var_1d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb5ae985", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " ds[var_1d].plot()\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "9e41066e", + "metadata": {}, + "source": [ + "#### 2-dimenssional basic plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09bd4adc", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==2, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_2 = df_info[(df_info.n_dim == 2) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2cdea1a9", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_2d = df_filter_2.var_name.values[0]\n", + " var_2d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5f48879", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " print(ds[var_2d].dims)\n", + " # ds[var_2d].plot()\n", + "\n", + " # conventionally, use \"time\" as x-axis\n", + " ds[var_2d].plot(x=\"time\")\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "7554b2a6", + "metadata": {}, + "source": [ + "#### qc-plotting (optional)\n", + "\n", + "Note: act qc plotting has more strict requirement, one of them is the associated qc variable need to have \"flag_masks\" attributes. Which is added by using ds = act.io.armfiles.read_netcdf(files_list), then ds.clean.cleanup()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71c2096f", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " ds_act = act.io.armfiles.read_netcdf(full_path)\n", + " print(type(ds_act))\n", + " ds_act.clean.cleanup()\n", + "\n", + " # or \n", + " # ds.clean.cleanup()\n", + "except Exception as e:\n", + " print(\"ERROR\", e)\n", + " ds_act = ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "844f8505", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter valid variables for ACT qc plotting\n", + "condition = (\"qc_\" + df_info.var_name).apply(lambda x: ds_act[x].attrs.get(\"flag_masks\") is not None \n", + " if x in list(ds.variables) else False)\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_3 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings))) &\n", + " condition\n", + " ]\n", + "df_filter_3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd38cc6", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "try:\n", + " qc_variable = df_filter_3.var_name.values[0]\n", + " print(qc_variable)\n", + "except Exception as e:\n", + " print(e)\n", + " \n", + "try:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + " qc_ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "d76e4d27", + "metadata": {}, + "source": [ + "#### bonus: choose variables to plot from a dropdown menu " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb733804", + "metadata": {}, + "outputs": [], + "source": [ + "# Valid variables filtering\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_4 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82d5c20d", + "metadata": {}, + "outputs": [], + "source": [ + "# example 1: using xarray plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2fc9aaa4", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# fig, ax = plt.subplots(figsize=(10, 4))\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "# fig.clear() # Remove old lines from plot and plot new one\n", + "# if len(ds[var].dims)==2:\n", + "# ds[var].plot(x=\"time\", add_colorbar=False)\n", + "# else:\n", + "# ds[var].plot()\n", + "# plt.grid()\n", + "# plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fabdd802", + "metadata": {}, + "outputs": [], + "source": [ + "# example 2: using act plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "962b4186", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "\n", + "\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "\n", + "# i_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + "# i_display.add_subplots((1,), figsize=(10, 4))\n", + "# ax = i_display.plot(var, subplot_index=(0,), set_title=f\"{var} ({ds_act[var].attrs['long_name']})\",)\n", + "\n", + "# ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "# ax.grid()\n", + "# plt.show()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/INLETCVI-AIR/aafinletcvi.c1.ipynb b/VAPs/quicklook/INLETCVI-AIR/aafinletcvi.c1.ipynb new file mode 100644 index 00000000..5f7787c7 --- /dev/null +++ b/VAPs/quicklook/INLETCVI-AIR/aafinletcvi.c1.ipynb @@ -0,0 +1,339 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# AAFINLETCVI.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/inletcvi-air) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'aafinletcvi'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2018-12-08', 'facility': 'F1', 'site': 'cor', 'start_date': '2018-11-04'}, {'end_date': '2018-02-19', 'facility': 'F1', 'site': 'ena', 'start_date': '2017-06-21'}, {'end_date': '2016-09-22', 'facility': 'F1', 'site': 'sgp', 'start_date': '2016-04-25'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'F1' )\n", + "\n", + "date_start = '2016-09-20'\n", + "date_end = '2016-09-22'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['cvi_cut_size', 'enhancement_factor']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'cvi_cut_size'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'cvi_cut_size'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/INTERPSONDE/INTERPSONDE_tutorial.ipynb b/VAPs/quicklook/INTERPSONDE/INTERPSONDE_tutorial.ipynb new file mode 100644 index 00000000..7c751ae1 --- /dev/null +++ b/VAPs/quicklook/INTERPSONDE/INTERPSONDE_tutorial.ipynb @@ -0,0 +1,838 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# INTERPOLATEDSONDE.C1 Notebook\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/interpsonde) for more information about this vap." + ] + }, + { + "cell_type": "markdown", + "id": "97097763", + "metadata": {}, + "source": [ + "In this notebook, we demonstrate the workflow to explore ARM vap data (using interpolatedsonde as an example.) Value-added products (VAPs) are higher-order data products that have been analyzed and processed to ease scientists’ use of ARM data in atmospheric research and global climate models. \n", + "Here is the main content we will cover." + ] + }, + { + "cell_type": "markdown", + "id": "eddec40f", + "metadata": {}, + "source": [ + "# Table of Content\n", + "## Access the data\n", + "* How to retrieve the data\n", + "* Data path and file name conventions\n", + "* Load data\n", + "## Explore the data\n", + "* NetCDF Data structure\n", + "* Xarray essentials\n", + "* Xarray Variable\n", + "## Plot the data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "import random\n", + "\n", + "import glob\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "2372d5be", + "metadata": {}, + "source": [ + "## Access the data" + ] + }, + { + "cell_type": "markdown", + "id": "5b0d9684", + "metadata": {}, + "source": [ + "### How to retrieve the data\n", + "We assume the path \"/data/archive\" is available where you are running this notebook. You can use `os.path.exists(\"/data/archive\")` to verify if the path exists at your machine.\n", + "\n", + "\n", + "### Data path and file name conventions\n", + "There several common terminologies regarding ARM data, for example, data-stream-name, data-level, site, facility, instrument, etc. (For more details, please see [ARM Data File Standards Version 1.2](https://www.google.com/search?q=arm+datastream+facility+cite+definition&rlz=1C1GCEJ_enUS1029US1029&ei=hb41ZICrBPukqtsPvMODIA&ved=0ahUKEwjAgeiS0aL-AhV7kmoFHbzhAAQQ4dUDCBA&uact=5&oq=arm+datastream+facility+cite+definition&gs_lcp=Cgxnd3Mtd2l6LXNlcnAQAzIFCAAQogQyBQgAEKIEMgUIABCiBDIFCAAQogQyBQgAEKIEOgoIABBHENYEELADOgoIIRCgARDDBBAKSgQIQRgAUJ4DWIoOYO0PaAFwAXgAgAGMAYgBuQmSAQMyLjmYAQCgAQHIAQjAAQE&sclient=gws-wiz-serp).) \n", + "\n", + "For example, this notebook is called `interpolatedsonde.c1`, where `interpolatedsonde` is the \"datastream name\", and the `{process.ds_class_level}}` is the \"data level\".\n", + "\n", + "This datastream also contains site `awr` and facility `M1`. (Note: individual datastream might have multiple site-facility pairs.)\n", + "In such a case, the data of this data-stream is stored at `/data/archive/awr/awrinterpolatedsondeM1.c1`, which is in the format of `//.`. We can use the following method to assign the data-stream directory `datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )`\n", + "\n", + "The data files under datastream_dir also follows naming conventions. But once reach the datastream_dir level, the most import file naming convention to differentiate the files is \"yyyyMMdd.hhmmss\", which comes handy to filter out files based on datetime. For example, we can use `glob.glob(f'{datastream_dir}/*.200709*.*')` to filter files in 2007 September.\n", + "\n", + "(Note: refer to the https://adc.arm.gov/discovery/#/ and https://adc.arm.gov/solr8/metadata/select API to explore ARM datastream and assoicated available sites and facilities\n", + "\n", + "Please see the following examples in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d7e9eb85", + "metadata": {}, + "outputs": [], + "source": [ + "# Verify if DATA_DIR path exists\n", + "DATA_DIR = \"/data/archive\"\n", + "os.path.exists(DATA_DIR)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "586993fd", + "metadata": {}, + "outputs": [], + "source": [ + "# Speicify datastream_dir following the path conventions and check its existence\n", + "DATASTREAM_NAME = \"interpolatedsonde\"\n", + "DATA_LEVEL = \"c1\"\n", + "site = \"awr\"\n", + "facility = \"M1\"\n", + "datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )\n", + "print(datastream_dir)\n", + "print(os.path.exists(datastream_dir))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0742f7c1", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: list 5 (random) files under datastream_dir\n", + "files = os.listdir(datastream_dir)\n", + "files[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39b98a36", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: get most recent file\n", + "list_of_files = glob.glob(f\"{datastream_dir}/*\") # * means all if need specific format then *.csv\n", + "latest_file = max(list_of_files, key=os.path.getctime)\n", + "latest_file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "902d514e", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus sort datastream files based on datetime\n", + "files = os.listdir(datastream_dir)\n", + "file_sorted = files.copy()\n", + "file_sorted.sort() \n", + "print(file_sorted[:5])\n", + "\n", + "# to reverse\n", + "file_sorted_reverse = files.copy() \n", + "file_sorted_reverse.sort(reverse=True)\n", + "print(file_sorted_reverse[:5])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec5923b2", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: pattern matching\n", + "# filter the 200709** files under datastream_dir\n", + "files_filter = glob.glob(f'{datastream_dir}/*.200709*.*')\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c4a4aa18", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: use armnotebook_utils.file_filter (TODO) to filter files based on datastream info \n" + ] + }, + { + "cell_type": "markdown", + "id": "cfeb9efc", + "metadata": {}, + "source": [ + "### Load data\n", + "The arm data is stored in [NetCDF](https://en.wikipedia.org/wiki/NetCDF#:~:text=%22NetCDF%20(network%20Common%20Data%20Form,format%20for%20representing%20scientific%20data.) format. We can use xarray's `open_dataset` method to load single file, or `open_mfdataset` to open multiple files. (Note: the latter will still return a single xarray dataset object by combining multiple files.)\n", + "\n", + "Note: open_dataset keeps the file handle open and lazy loads its contents. All parameters are passed directly to open_dataset. It is a preferable method over load_dataset for memory-effiency. (more details, please see [xarray.open_dataset](https://docs.xarray.dev/en/stable/generated/xarray.open_dataset.html))\n", + "\n", + "See the following example in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a440a329", + "metadata": {}, + "outputs": [], + "source": [ + "# open single file\n", + "full_path = latest_file\n", + "print(full_path)\n", + "ds_single = xr.open_dataset(full_path)\n", + "ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0143a3d", + "metadata": {}, + "outputs": [], + "source": [ + "print(ds_single)\n", + "print(type(ds_single))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c0f8939", + "metadata": {}, + "outputs": [], + "source": [ + "# open multiple files\n", + "n_files = 3\n", + "full_paths = [os.path.join(datastream_dir, f_path) for f_path in file_sorted_reverse[:n_files]]\n", + "print(full_paths)\n", + "try: # Note: sometimes multiple files cannot be merged, so we used try except here.\n", + " ds_mutiple = xr.open_mfdataset(full_paths)\n", + " # ds_mutiple\n", + " print(type(ds_mutiple))\n", + " print(ds_mutiple)\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "3fa59943", + "metadata": {}, + "source": [ + "## Explore the data" + ] + }, + { + "cell_type": "markdown", + "id": "571f69b9", + "metadata": {}, + "source": [ + "### NetCDF Data structure \n", + "\n", + "\n", + "(If you are confident with NetCDF basics and xarray essentials, feel free to skip this session.)\n", + "Before we dive into data exploration, there are some eseential concpets we should be familiar with (shown below): \n", + "* Dataset\n", + "* Data array\n", + "* Variable\n", + "* Dimenssion\n", + "* Coordinate\n", + "* Data Type\n", + "* Meta Data (Attributes)\n", + "\n", + "We will not go into details about NetCDF basics and here are some references you might find helpful\n", + "* [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#)\n", + "* [Network Common Data Form (NetCDF)](https://www.unidata.ucar.edu/software/netcdf/) and [A Brief History of (netCDF) Time](https://www.unidata.ucar.edu/software/netcdf/time/recs.html)\n", + "\n", + "\n", + "\n", + "### Xarray essentials\n", + "Earlier we introduced the xarray pacakge and used `open_dataset` and `open_mfdataset` to retrieve NetCDF data file as an xarray Dataset object. Recall that you can review the data in a notebook by using `print(ds)` or `ds`. \n", + "\n", + "Using xarray to retrieve the aforementioned NetCDF basics is straightforwared. In general,\n", + "* Dataset: `ds`\n", + "* Data array: `ds.variables`\n", + "* Variable: `ds.ds.variables`\n", + "* Dimenssion: `ds.dims`\n", + "* Coordinate: `ds.coords`\n", + "* Data Type: `type`\n", + "* Meta Data (Attributes)\n", + "\n", + "Also, here are some references if you are new to xarray\n", + "* [Xarray in 45 minutes](https://tutorial.xarray.dev/overview/xarray-in-45-min.html)\n", + "* [Handling NetCDF Files using XArray for Absolute Beginners](https://towardsdatascience.com/handling-netcdf-files-using-xarray-for-absolute-beginners-111a8ab4463f)\n", + "\n", + "Try the following commands in action\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b3eac323", + "metadata": {}, + "outputs": [], + "source": [ + "ds = ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77ecf85d", + "metadata": {}, + "outputs": [], + "source": [ + "# Dataset \n", + "ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25e7de09", + "metadata": {}, + "outputs": [], + "source": [ + "# Data array, Variable\n", + "# Note: the info can be overwhelming. Do not attempt to grasp everything at the first glance.\n", + "ds.variables" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c41b67e", + "metadata": {}, + "outputs": [], + "source": [ + "# dimenssions\n", + "ds.dims" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "156f1dfc", + "metadata": {}, + "outputs": [], + "source": [ + "# coordinates\n", + "ds.coords" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "277d6064", + "metadata": {}, + "outputs": [], + "source": [ + "# Meta Data (Attributes)\n", + "ds.attrs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d334681f", + "metadata": {}, + "outputs": [], + "source": [ + "# type\n", + "print(type(ds))\n", + "print(type(ds.variables))\n", + "print(type(ds.dims))\n", + "print(type(ds.coords))\n", + "print(type(ds.attrs))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "643399d6", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: retrieve variable names only\n", + "list(ds.variables)" + ] + }, + { + "cell_type": "markdown", + "id": "1cf36878", + "metadata": {}, + "source": [ + "#### Discussion: variable vs. coordinates vs. dimenssions. \n", + "From [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#):\n", + "\n", + "* Dimession: A dimension may be used to represent a real physical dimension, for example, time, latitude, longitude, or height. A dimension might also be used to index other quantities, for example station or model-run-number.\n", + "A netCDF dimension has both a name and a length. A dimension length is an arbitrary positive integer, except that one dimension in a netCDF dataset can have the length UNLIMITED.\n", + "\n", + "* Variables: Variables are used to store the bulk of the data in a netCDF dataset. A variable represents an array of values of the same type. A scalar value is treated as a 0-dimensional array. A variable has a name, a data type, and a shape described by its list of dimensions specified when the variable is created. A variable may also have associated attributes, which may be added, deleted or changed after the variable is created.\n", + "\n", + "* Coordinate (Variables): It is legal for a variable to have the same name as a dimension. Such variables have no special meaning to the netCDF library. However there is a convention that such variables should be treated in a special way by software using this library.\n", + "A variable with the same name as a dimension is called a coordinate variable. It typically defines a physical coordinate corresponding to that dimension.\n", + "\n", + "\n", + "Tips: By definition, coordinates and dimenssions are also variable. For example ds.time is a coordinate but it is also a special variable. But in practice, when we talk about variable, it implies regular/non-coordinate variable. We will use this convention for the remaining notebook. " + ] + }, + { + "cell_type": "markdown", + "id": "d47120f9", + "metadata": {}, + "source": [ + "### Xarray Variable\n", + "\n", + "\n", + "We can use `ds.variables` to access varialbes of a dataset. But many times viewing print-out the whole datasets can be overwhemling. Instead, we would work on the individual variable (also called Xarray data array.) using the `ds[var_name]` syntax, where `var_name` is the name of the variable. See the following examples in action.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5a57e136", + "metadata": {}, + "outputs": [], + "source": [ + "print(type(ds[\"time\"]))\n", + "ds[\"time\"]" + ] + }, + { + "cell_type": "markdown", + "id": "51ec643e", + "metadata": {}, + "source": [ + "#### Variable properties\n", + "For individual varible, the following properties are mostly used with the assocaited xarray method to retrieve them. (assuming var = ds[var_name])\n", + "* name: `var.name`\n", + "* data content: `var.data`\n", + "* attributes: `var.attrs`\n", + "* dimenstions: `var.dims`\n", + "* data type: `var.data.dtype`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a632a525", + "metadata": {}, + "outputs": [], + "source": [ + "var_name = \"time\"\n", + "var = ds[var_name]\n", + "\n", + "print(\"var.name: \\n\", var.name, \"\\n\")\n", + "print(\"var.data: \\n\", var.data, \"\\n\")\n", + "print(\"var.attrs: \\n\", var.attrs, \"\\n\")\n", + "print(\"var.dims: \\n\", var.dims, \"\\n\")\n", + "print(\"var.data.dtype: \\n\", var.data.dtype, \"\\n\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b256b07f", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: indivdual variable info table (collect and display variable info as a tabular format)\n", + "df_info = pd.DataFrame()\n", + "df_info[\"var_name\"] = list(ds.variables)\n", + "df_info[\"dims\"] = df_info.var_name.apply(lambda x: ds[x].dims)\n", + "df_info[\"is_dim\"] = df_info.var_name.apply(lambda x: x in ds.dims)\n", + "df_info[\"n_dim\"] = df_info.var_name.apply(lambda x: len(ds[x].dims))\n", + "df_info[\"attrs\"] = df_info.var_name.apply(lambda x: ds[x].attrs)\n", + "df_info[\"dtype\"] = df_info.var_name.apply(lambda x: ds[x].data.dtype)\n", + "df_info" + ] + }, + { + "cell_type": "markdown", + "id": "e6a36b96", + "metadata": {}, + "source": [ + "### Data cleaning/Preprocessing (skipped)\n", + "Data cleaning and preprocessing is an important stage in the the data analysis pipeline. However it will be out of the scope of this notebook. For more information about data cleaning and preprocessing basics, here are some references. \n", + "* [Xarray Fundamentals](https://earth-env-data-science.github.io/lectures/xarray/xarray.html)\n", + "* [Xarray Tutorial — Pangeo Gallery documentation](http://gallery.pangeo.io/repos/pangeo-data/pangeo-tutorial-gallery/xarray.html)\n", + "* [Pythonic Data Cleaning With pandas and NumPy](https://realpython.com/python-data-cleaning-numpy-pandas/)\n", + "* [Pandas - Cleaning Data](https://www.w3schools.com/python/pandas/pandas_cleaning.asp)" + ] + }, + { + "cell_type": "markdown", + "id": "ae6e1a69", + "metadata": {}, + "source": [ + "### Plotting\n", + "Data visualization (or plotting) is another important data analysis topic and deserves its own discussion. Here in this notebook we will only demonstrate basic tool for simple data visualization tasks.\n", + "\n", + "Here are some reference you might find useful:\n", + "* xarray plotting: https://docs.xarray.dev/en/stable/user-guide/plotting.html\n", + "* Atmospheric Community Toolkit (ACT): https://arm-doe.github.io/ACT/index.html\n", + "\n", + "Note: this notebook is auto-generated using a template. It uses a general idea to select variable(s) to plot and is not customized to each indivdual notebook. Feel free to change the variables in intrrests, especially certain figure is failed to plot." + ] + }, + { + "cell_type": "markdown", + "id": "7e417fb2", + "metadata": {}, + "source": [ + "#### 1-dimenssional basic time series plot\n", + "\n", + "For the following plot we would like to find variables such that\n", + "* it has one and only one dimession\n", + "* \"time\" is its coordinate variable\n", + "* it is not a dimenssion itself,\n", + "* it is not a special variable with substrings within [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "\n", + "Please see the following example in action. (For more details about pandas filtering, please see the following references.)\n", + "* [pandas: multiple conditions while indexing data frame](https://stackoverflow.com/questions/22591174/pandas-multiple-conditions-while-indexing-data-frame-unexpected-behavior)\n", + "* [pandas.DataFrame.apply](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.apply.html)\n", + "* [How to test if a string contains one of the substrings in a list...](https://stackoverflow.com/questions/26577516/how-to-test-if-a-string-contains-one-of-the-substrings-in-a-list-in-pandas" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30edac2d", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==1, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter = df_info[(df_info.n_dim == 1) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4777a659", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_1d = df_filter.var_name.values[0]\n", + " var_1d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb5ae985", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " ds[var_1d].plot()\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "9e41066e", + "metadata": {}, + "source": [ + "#### 2-dimenssional basic plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09bd4adc", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==2, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_2 = df_info[(df_info.n_dim == 2) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2cdea1a9", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_2d = df_filter_2.var_name.values[0]\n", + " var_2d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5f48879", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " print(ds[var_2d].dims)\n", + " # ds[var_2d].plot()\n", + "\n", + " # conventionally, use \"time\" as x-axis\n", + " ds[var_2d].plot(x=\"time\")\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "7554b2a6", + "metadata": {}, + "source": [ + "#### qc-plotting (optional)\n", + "\n", + "Note: act qc plotting has more strict requirement, one of them is the associated qc variable need to have \"flag_masks\" attributes. Which is added by using ds = act.io.armfiles.read_netcdf(files_list), then ds.clean.cleanup()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71c2096f", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " ds_act = act.io.armfiles.read_netcdf(full_path)\n", + " print(type(ds_act))\n", + " ds_act.clean.cleanup()\n", + "\n", + " # or \n", + " # ds.clean.cleanup()\n", + "except Exception as e:\n", + " print(\"ERROR\", e)\n", + " ds_act = ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "844f8505", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter valid variables for ACT qc plotting\n", + "condition = (\"qc_\" + df_info.var_name).apply(lambda x: ds_act[x].attrs.get(\"flag_masks\") is not None \n", + " if x in list(ds.variables) else False)\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_3 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings))) &\n", + " condition\n", + " ]\n", + "df_filter_3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd38cc6", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "try:\n", + " qc_variable = df_filter_3.var_name.values[0]\n", + " print(qc_variable)\n", + "except Exception as e:\n", + " print(e)\n", + " \n", + "try:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + " qc_ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "d76e4d27", + "metadata": {}, + "source": [ + "#### bonus: choose variables to plot from a dropdown menu " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb733804", + "metadata": {}, + "outputs": [], + "source": [ + "# Valid variables filtering\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_4 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82d5c20d", + "metadata": {}, + "outputs": [], + "source": [ + "# example 1: using xarray plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2fc9aaa4", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# fig, ax = plt.subplots(figsize=(10, 4))\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "# fig.clear() # Remove old lines from plot and plot new one\n", + "# if len(ds[var].dims)==2:\n", + "# ds[var].plot(x=\"time\", add_colorbar=False)\n", + "# else:\n", + "# ds[var].plot()\n", + "# plt.grid()\n", + "# plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fabdd802", + "metadata": {}, + "outputs": [], + "source": [ + "# example 2: using act plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "962b4186", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "\n", + "\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "\n", + "# i_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + "# i_display.add_subplots((1,), figsize=(10, 4))\n", + "# ax = i_display.plot(var, subplot_index=(0,), set_title=f\"{var} ({ds_act[var].attrs['long_name']})\",)\n", + "\n", + "# ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "# ax.grid()\n", + "# plt.show()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/INTERPSONDE/interpolatedsonde.c1.ipynb b/VAPs/quicklook/INTERPSONDE/interpolatedsonde.c1.ipynb new file mode 100644 index 00000000..d0faebd8 --- /dev/null +++ b/VAPs/quicklook/INTERPSONDE/interpolatedsonde.c1.ipynb @@ -0,0 +1,339 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# INTERPOLATEDSONDE.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/interpsonde) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'interpolatedsonde'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2017-01-04', 'facility': 'M1', 'site': 'awr', 'start_date': '2015-11-30'}, {'end_date': '2016-01-16', 'facility': 'S1', 'site': 'awr', 'start_date': '2015-12-04'}, {'end_date': '2017-11-01', 'facility': 'S1', 'site': 'asi', 'start_date': '2016-05-01'}, {'end_date': '2020-05-31', 'facility': 'M1', 'site': 'anx', 'start_date': '2019-12-01'}, {'end_date': '2023-06-17', 'facility': 'M1', 'site': 'guc', 'start_date': '2021-09-01'}, {'end_date': '2023-12-11', 'facility': 'C1', 'site': 'ena', 'start_date': '2013-09-28'}, {'end_date': '2022-10-02', 'facility': 'M1', 'site': 'hou', 'start_date': '2021-10-01'}, {'end_date': '2023-12-11', 'facility': 'M1', 'site': 'epc', 'start_date': '2023-02-06'}, {'end_date': '2015-11-30', 'facility': 'M1', 'site': 'mao', 'start_date': '2014-01-01'}, {'end_date': '2018-03-24', 'facility': 'M1', 'site': 'mar', 'start_date': '2017-10-31'}, {'end_date': '2013-10-02', 'facility': 'M1', 'site': 'mag', 'start_date': '2012-09-30'}, {'end_date': '2019-05-01', 'facility': 'M1', 'site': 'cor', 'start_date': '2018-09-28'}, {'end_date': '2019-05-01', 'facility': 'S1', 'site': 'cor', 'start_date': '2018-09-23'}, {'end_date': '2012-04-09', 'facility': 'M1', 'site': 'gan', 'start_date': '2011-09-15'}, {'end_date': '2013-06-29', 'facility': 'M1', 'site': 'pvc', 'start_date': '2012-07-01'}, {'end_date': '2020-10-01', 'facility': 'M1', 'site': 'mos', 'start_date': '2019-10-11'}, {'end_date': '2023-12-11', 'facility': 'C1', 'site': 'nsa', 'start_date': '2002-04-28'}, {'end_date': '2021-06-16', 'facility': 'M1', 'site': 'oli', 'start_date': '2013-10-01'}, {'end_date': '2023-12-11', 'facility': 'C1', 'site': 'sgp', 'start_date': '1999-07-23'}, {'end_date': '2014-09-10', 'facility': 'M1', 'site': 'tmp', 'start_date': '2014-02-01'}, {'end_date': '2014-07-09', 'facility': 'C1', 'site': 'twp', 'start_date': '2001-04-05'}, {'end_date': '2013-09-08', 'facility': 'C2', 'site': 'twp', 'start_date': '1998-10-21'}, {'end_date': '2015-01-16', 'facility': 'C3', 'site': 'twp', 'start_date': '2002-04-03'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2023-12-09'\n", + "date_end = '2023-12-11'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['temp', 'rh', 'vap_pres']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'precip'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'temp'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/KAZRARSCL/KAZRARSCL_tutorial.ipynb b/VAPs/quicklook/KAZRARSCL/KAZRARSCL_tutorial.ipynb new file mode 100644 index 00000000..021f11ec --- /dev/null +++ b/VAPs/quicklook/KAZRARSCL/KAZRARSCL_tutorial.ipynb @@ -0,0 +1,838 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# ARSCLKAZR1KOLLIAS.C1 Notebook\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/kazrarscl) for more information about this vap." + ] + }, + { + "cell_type": "markdown", + "id": "97097763", + "metadata": {}, + "source": [ + "In this notebook, we demonstrate the workflow to explore ARM vap data (using arsclkazr1kollias as an example.) Value-added products (VAPs) are higher-order data products that have been analyzed and processed to ease scientists’ use of ARM data in atmospheric research and global climate models. \n", + "Here is the main content we will cover." + ] + }, + { + "cell_type": "markdown", + "id": "eddec40f", + "metadata": {}, + "source": [ + "# Table of Content\n", + "## Access the data\n", + "* How to retrieve the data\n", + "* Data path and file name conventions\n", + "* Load data\n", + "## Explore the data\n", + "* NetCDF Data structure\n", + "* Xarray essentials\n", + "* Xarray Variable\n", + "## Plot the data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "import random\n", + "\n", + "import glob\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "2372d5be", + "metadata": {}, + "source": [ + "## Access the data" + ] + }, + { + "cell_type": "markdown", + "id": "5b0d9684", + "metadata": {}, + "source": [ + "### How to retrieve the data\n", + "We assume the path \"/data/archive\" is available where you are running this notebook. You can use `os.path.exists(\"/data/archive\")` to verify if the path exists at your machine.\n", + "\n", + "\n", + "### Data path and file name conventions\n", + "There several common terminologies regarding ARM data, for example, data-stream-name, data-level, site, facility, instrument, etc. (For more details, please see [ARM Data File Standards Version 1.2](https://www.google.com/search?q=arm+datastream+facility+cite+definition&rlz=1C1GCEJ_enUS1029US1029&ei=hb41ZICrBPukqtsPvMODIA&ved=0ahUKEwjAgeiS0aL-AhV7kmoFHbzhAAQQ4dUDCBA&uact=5&oq=arm+datastream+facility+cite+definition&gs_lcp=Cgxnd3Mtd2l6LXNlcnAQAzIFCAAQogQyBQgAEKIEMgUIABCiBDIFCAAQogQyBQgAEKIEOgoIABBHENYEELADOgoIIRCgARDDBBAKSgQIQRgAUJ4DWIoOYO0PaAFwAXgAgAGMAYgBuQmSAQMyLjmYAQCgAQHIAQjAAQE&sclient=gws-wiz-serp).) \n", + "\n", + "For example, this notebook is called `arsclkazr1kollias.c1`, where `arsclkazr1kollias` is the \"datastream name\", and the `{process.ds_class_level}}` is the \"data level\".\n", + "\n", + "This datastream also contains site `anx` and facility `M1`. (Note: individual datastream might have multiple site-facility pairs.)\n", + "In such a case, the data of this data-stream is stored at `/data/archive/anx/anxarsclkazr1kolliasM1.c1`, which is in the format of `//.`. We can use the following method to assign the data-stream directory `datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )`\n", + "\n", + "The data files under datastream_dir also follows naming conventions. But once reach the datastream_dir level, the most import file naming convention to differentiate the files is \"yyyyMMdd.hhmmss\", which comes handy to filter out files based on datetime. For example, we can use `glob.glob(f'{datastream_dir}/*.200709*.*')` to filter files in 2007 September.\n", + "\n", + "(Note: refer to the https://adc.arm.gov/discovery/#/ and https://adc.arm.gov/solr8/metadata/select API to explore ARM datastream and assoicated available sites and facilities\n", + "\n", + "Please see the following examples in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d7e9eb85", + "metadata": {}, + "outputs": [], + "source": [ + "# Verify if DATA_DIR path exists\n", + "DATA_DIR = \"/data/archive\"\n", + "os.path.exists(DATA_DIR)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "586993fd", + "metadata": {}, + "outputs": [], + "source": [ + "# Speicify datastream_dir following the path conventions and check its existence\n", + "DATASTREAM_NAME = \"arsclkazr1kollias\"\n", + "DATA_LEVEL = \"c1\"\n", + "site = \"anx\"\n", + "facility = \"M1\"\n", + "datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )\n", + "print(datastream_dir)\n", + "print(os.path.exists(datastream_dir))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0742f7c1", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: list 5 (random) files under datastream_dir\n", + "files = os.listdir(datastream_dir)\n", + "files[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39b98a36", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: get most recent file\n", + "list_of_files = glob.glob(f\"{datastream_dir}/*\") # * means all if need specific format then *.csv\n", + "latest_file = max(list_of_files, key=os.path.getctime)\n", + "latest_file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "902d514e", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus sort datastream files based on datetime\n", + "files = os.listdir(datastream_dir)\n", + "file_sorted = files.copy()\n", + "file_sorted.sort() \n", + "print(file_sorted[:5])\n", + "\n", + "# to reverse\n", + "file_sorted_reverse = files.copy() \n", + "file_sorted_reverse.sort(reverse=True)\n", + "print(file_sorted_reverse[:5])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec5923b2", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: pattern matching\n", + "# filter the 200709** files under datastream_dir\n", + "files_filter = glob.glob(f'{datastream_dir}/*.200709*.*')\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c4a4aa18", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: use armnotebook_utils.file_filter (TODO) to filter files based on datastream info \n" + ] + }, + { + "cell_type": "markdown", + "id": "cfeb9efc", + "metadata": {}, + "source": [ + "### Load data\n", + "The arm data is stored in [NetCDF](https://en.wikipedia.org/wiki/NetCDF#:~:text=%22NetCDF%20(network%20Common%20Data%20Form,format%20for%20representing%20scientific%20data.) format. We can use xarray's `open_dataset` method to load single file, or `open_mfdataset` to open multiple files. (Note: the latter will still return a single xarray dataset object by combining multiple files.)\n", + "\n", + "Note: open_dataset keeps the file handle open and lazy loads its contents. All parameters are passed directly to open_dataset. It is a preferable method over load_dataset for memory-effiency. (more details, please see [xarray.open_dataset](https://docs.xarray.dev/en/stable/generated/xarray.open_dataset.html))\n", + "\n", + "See the following example in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a440a329", + "metadata": {}, + "outputs": [], + "source": [ + "# open single file\n", + "full_path = latest_file\n", + "print(full_path)\n", + "ds_single = xr.open_dataset(full_path)\n", + "ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0143a3d", + "metadata": {}, + "outputs": [], + "source": [ + "print(ds_single)\n", + "print(type(ds_single))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c0f8939", + "metadata": {}, + "outputs": [], + "source": [ + "# open multiple files\n", + "n_files = 3\n", + "full_paths = [os.path.join(datastream_dir, f_path) for f_path in file_sorted_reverse[:n_files]]\n", + "print(full_paths)\n", + "try: # Note: sometimes multiple files cannot be merged, so we used try except here.\n", + " ds_mutiple = xr.open_mfdataset(full_paths)\n", + " # ds_mutiple\n", + " print(type(ds_mutiple))\n", + " print(ds_mutiple)\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "3fa59943", + "metadata": {}, + "source": [ + "## Explore the data" + ] + }, + { + "cell_type": "markdown", + "id": "571f69b9", + "metadata": {}, + "source": [ + "### NetCDF Data structure \n", + "\n", + "\n", + "(If you are confident with NetCDF basics and xarray essentials, feel free to skip this session.)\n", + "Before we dive into data exploration, there are some eseential concpets we should be familiar with (shown below): \n", + "* Dataset\n", + "* Data array\n", + "* Variable\n", + "* Dimenssion\n", + "* Coordinate\n", + "* Data Type\n", + "* Meta Data (Attributes)\n", + "\n", + "We will not go into details about NetCDF basics and here are some references you might find helpful\n", + "* [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#)\n", + "* [Network Common Data Form (NetCDF)](https://www.unidata.ucar.edu/software/netcdf/) and [A Brief History of (netCDF) Time](https://www.unidata.ucar.edu/software/netcdf/time/recs.html)\n", + "\n", + "\n", + "\n", + "### Xarray essentials\n", + "Earlier we introduced the xarray pacakge and used `open_dataset` and `open_mfdataset` to retrieve NetCDF data file as an xarray Dataset object. Recall that you can review the data in a notebook by using `print(ds)` or `ds`. \n", + "\n", + "Using xarray to retrieve the aforementioned NetCDF basics is straightforwared. In general,\n", + "* Dataset: `ds`\n", + "* Data array: `ds.variables`\n", + "* Variable: `ds.ds.variables`\n", + "* Dimenssion: `ds.dims`\n", + "* Coordinate: `ds.coords`\n", + "* Data Type: `type`\n", + "* Meta Data (Attributes)\n", + "\n", + "Also, here are some references if you are new to xarray\n", + "* [Xarray in 45 minutes](https://tutorial.xarray.dev/overview/xarray-in-45-min.html)\n", + "* [Handling NetCDF Files using XArray for Absolute Beginners](https://towardsdatascience.com/handling-netcdf-files-using-xarray-for-absolute-beginners-111a8ab4463f)\n", + "\n", + "Try the following commands in action\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b3eac323", + "metadata": {}, + "outputs": [], + "source": [ + "ds = ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77ecf85d", + "metadata": {}, + "outputs": [], + "source": [ + "# Dataset \n", + "ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25e7de09", + "metadata": {}, + "outputs": [], + "source": [ + "# Data array, Variable\n", + "# Note: the info can be overwhelming. Do not attempt to grasp everything at the first glance.\n", + "ds.variables" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c41b67e", + "metadata": {}, + "outputs": [], + "source": [ + "# dimenssions\n", + "ds.dims" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "156f1dfc", + "metadata": {}, + "outputs": [], + "source": [ + "# coordinates\n", + "ds.coords" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "277d6064", + "metadata": {}, + "outputs": [], + "source": [ + "# Meta Data (Attributes)\n", + "ds.attrs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d334681f", + "metadata": {}, + "outputs": [], + "source": [ + "# type\n", + "print(type(ds))\n", + "print(type(ds.variables))\n", + "print(type(ds.dims))\n", + "print(type(ds.coords))\n", + "print(type(ds.attrs))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "643399d6", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: retrieve variable names only\n", + "list(ds.variables)" + ] + }, + { + "cell_type": "markdown", + "id": "1cf36878", + "metadata": {}, + "source": [ + "#### Discussion: variable vs. coordinates vs. dimenssions. \n", + "From [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#):\n", + "\n", + "* Dimession: A dimension may be used to represent a real physical dimension, for example, time, latitude, longitude, or height. A dimension might also be used to index other quantities, for example station or model-run-number.\n", + "A netCDF dimension has both a name and a length. A dimension length is an arbitrary positive integer, except that one dimension in a netCDF dataset can have the length UNLIMITED.\n", + "\n", + "* Variables: Variables are used to store the bulk of the data in a netCDF dataset. A variable represents an array of values of the same type. A scalar value is treated as a 0-dimensional array. A variable has a name, a data type, and a shape described by its list of dimensions specified when the variable is created. A variable may also have associated attributes, which may be added, deleted or changed after the variable is created.\n", + "\n", + "* Coordinate (Variables): It is legal for a variable to have the same name as a dimension. Such variables have no special meaning to the netCDF library. However there is a convention that such variables should be treated in a special way by software using this library.\n", + "A variable with the same name as a dimension is called a coordinate variable. It typically defines a physical coordinate corresponding to that dimension.\n", + "\n", + "\n", + "Tips: By definition, coordinates and dimenssions are also variable. For example ds.time is a coordinate but it is also a special variable. But in practice, when we talk about variable, it implies regular/non-coordinate variable. We will use this convention for the remaining notebook. " + ] + }, + { + "cell_type": "markdown", + "id": "d47120f9", + "metadata": {}, + "source": [ + "### Xarray Variable\n", + "\n", + "\n", + "We can use `ds.variables` to access varialbes of a dataset. But many times viewing print-out the whole datasets can be overwhemling. Instead, we would work on the individual variable (also called Xarray data array.) using the `ds[var_name]` syntax, where `var_name` is the name of the variable. See the following examples in action.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5a57e136", + "metadata": {}, + "outputs": [], + "source": [ + "print(type(ds[\"time\"]))\n", + "ds[\"time\"]" + ] + }, + { + "cell_type": "markdown", + "id": "51ec643e", + "metadata": {}, + "source": [ + "#### Variable properties\n", + "For individual varible, the following properties are mostly used with the assocaited xarray method to retrieve them. (assuming var = ds[var_name])\n", + "* name: `var.name`\n", + "* data content: `var.data`\n", + "* attributes: `var.attrs`\n", + "* dimenstions: `var.dims`\n", + "* data type: `var.data.dtype`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a632a525", + "metadata": {}, + "outputs": [], + "source": [ + "var_name = \"time\"\n", + "var = ds[var_name]\n", + "\n", + "print(\"var.name: \\n\", var.name, \"\\n\")\n", + "print(\"var.data: \\n\", var.data, \"\\n\")\n", + "print(\"var.attrs: \\n\", var.attrs, \"\\n\")\n", + "print(\"var.dims: \\n\", var.dims, \"\\n\")\n", + "print(\"var.data.dtype: \\n\", var.data.dtype, \"\\n\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b256b07f", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: indivdual variable info table (collect and display variable info as a tabular format)\n", + "df_info = pd.DataFrame()\n", + "df_info[\"var_name\"] = list(ds.variables)\n", + "df_info[\"dims\"] = df_info.var_name.apply(lambda x: ds[x].dims)\n", + "df_info[\"is_dim\"] = df_info.var_name.apply(lambda x: x in ds.dims)\n", + "df_info[\"n_dim\"] = df_info.var_name.apply(lambda x: len(ds[x].dims))\n", + "df_info[\"attrs\"] = df_info.var_name.apply(lambda x: ds[x].attrs)\n", + "df_info[\"dtype\"] = df_info.var_name.apply(lambda x: ds[x].data.dtype)\n", + "df_info" + ] + }, + { + "cell_type": "markdown", + "id": "e6a36b96", + "metadata": {}, + "source": [ + "### Data cleaning/Preprocessing (skipped)\n", + "Data cleaning and preprocessing is an important stage in the the data analysis pipeline. However it will be out of the scope of this notebook. For more information about data cleaning and preprocessing basics, here are some references. \n", + "* [Xarray Fundamentals](https://earth-env-data-science.github.io/lectures/xarray/xarray.html)\n", + "* [Xarray Tutorial — Pangeo Gallery documentation](http://gallery.pangeo.io/repos/pangeo-data/pangeo-tutorial-gallery/xarray.html)\n", + "* [Pythonic Data Cleaning With pandas and NumPy](https://realpython.com/python-data-cleaning-numpy-pandas/)\n", + "* [Pandas - Cleaning Data](https://www.w3schools.com/python/pandas/pandas_cleaning.asp)" + ] + }, + { + "cell_type": "markdown", + "id": "ae6e1a69", + "metadata": {}, + "source": [ + "### Plotting\n", + "Data visualization (or plotting) is another important data analysis topic and deserves its own discussion. Here in this notebook we will only demonstrate basic tool for simple data visualization tasks.\n", + "\n", + "Here are some reference you might find useful:\n", + "* xarray plotting: https://docs.xarray.dev/en/stable/user-guide/plotting.html\n", + "* Atmospheric Community Toolkit (ACT): https://arm-doe.github.io/ACT/index.html\n", + "\n", + "Note: this notebook is auto-generated using a template. It uses a general idea to select variable(s) to plot and is not customized to each indivdual notebook. Feel free to change the variables in intrrests, especially certain figure is failed to plot." + ] + }, + { + "cell_type": "markdown", + "id": "7e417fb2", + "metadata": {}, + "source": [ + "#### 1-dimenssional basic time series plot\n", + "\n", + "For the following plot we would like to find variables such that\n", + "* it has one and only one dimession\n", + "* \"time\" is its coordinate variable\n", + "* it is not a dimenssion itself,\n", + "* it is not a special variable with substrings within [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "\n", + "Please see the following example in action. (For more details about pandas filtering, please see the following references.)\n", + "* [pandas: multiple conditions while indexing data frame](https://stackoverflow.com/questions/22591174/pandas-multiple-conditions-while-indexing-data-frame-unexpected-behavior)\n", + "* [pandas.DataFrame.apply](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.apply.html)\n", + "* [How to test if a string contains one of the substrings in a list...](https://stackoverflow.com/questions/26577516/how-to-test-if-a-string-contains-one-of-the-substrings-in-a-list-in-pandas" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30edac2d", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==1, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter = df_info[(df_info.n_dim == 1) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4777a659", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_1d = df_filter.var_name.values[0]\n", + " var_1d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb5ae985", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " ds[var_1d].plot()\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "9e41066e", + "metadata": {}, + "source": [ + "#### 2-dimenssional basic plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09bd4adc", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==2, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_2 = df_info[(df_info.n_dim == 2) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2cdea1a9", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_2d = df_filter_2.var_name.values[0]\n", + " var_2d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5f48879", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " print(ds[var_2d].dims)\n", + " # ds[var_2d].plot()\n", + "\n", + " # conventionally, use \"time\" as x-axis\n", + " ds[var_2d].plot(x=\"time\")\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "7554b2a6", + "metadata": {}, + "source": [ + "#### qc-plotting (optional)\n", + "\n", + "Note: act qc plotting has more strict requirement, one of them is the associated qc variable need to have \"flag_masks\" attributes. Which is added by using ds = act.io.armfiles.read_netcdf(files_list), then ds.clean.cleanup()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71c2096f", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " ds_act = act.io.armfiles.read_netcdf(full_path)\n", + " print(type(ds_act))\n", + " ds_act.clean.cleanup()\n", + "\n", + " # or \n", + " # ds.clean.cleanup()\n", + "except Exception as e:\n", + " print(\"ERROR\", e)\n", + " ds_act = ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "844f8505", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter valid variables for ACT qc plotting\n", + "condition = (\"qc_\" + df_info.var_name).apply(lambda x: ds_act[x].attrs.get(\"flag_masks\") is not None \n", + " if x in list(ds.variables) else False)\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_3 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings))) &\n", + " condition\n", + " ]\n", + "df_filter_3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd38cc6", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "try:\n", + " qc_variable = df_filter_3.var_name.values[0]\n", + " print(qc_variable)\n", + "except Exception as e:\n", + " print(e)\n", + " \n", + "try:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + " qc_ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "d76e4d27", + "metadata": {}, + "source": [ + "#### bonus: choose variables to plot from a dropdown menu " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb733804", + "metadata": {}, + "outputs": [], + "source": [ + "# Valid variables filtering\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_4 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82d5c20d", + "metadata": {}, + "outputs": [], + "source": [ + "# example 1: using xarray plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2fc9aaa4", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# fig, ax = plt.subplots(figsize=(10, 4))\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "# fig.clear() # Remove old lines from plot and plot new one\n", + "# if len(ds[var].dims)==2:\n", + "# ds[var].plot(x=\"time\", add_colorbar=False)\n", + "# else:\n", + "# ds[var].plot()\n", + "# plt.grid()\n", + "# plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fabdd802", + "metadata": {}, + "outputs": [], + "source": [ + "# example 2: using act plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "962b4186", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "\n", + "\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "\n", + "# i_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + "# i_display.add_subplots((1,), figsize=(10, 4))\n", + "# ax = i_display.plot(var, subplot_index=(0,), set_title=f\"{var} ({ds_act[var].attrs['long_name']})\",)\n", + "\n", + "# ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "# ax.grid()\n", + "# plt.show()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/KAZRARSCL/arsclkazr1kollias.c1.ipynb b/VAPs/quicklook/KAZRARSCL/arsclkazr1kollias.c1.ipynb new file mode 100644 index 00000000..6b5f5017 --- /dev/null +++ b/VAPs/quicklook/KAZRARSCL/arsclkazr1kollias.c1.ipynb @@ -0,0 +1,339 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# ARSCLKAZR1KOLLIAS.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/kazrarscl) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'arsclkazr1kollias'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2020-05-31', 'facility': 'M1', 'site': 'anx', 'start_date': '2019-12-01'}, {'end_date': '2014-02-07', 'facility': 'C1', 'site': 'nsa', 'start_date': '2011-11-11'}, {'end_date': '2019-04-30', 'facility': 'M1', 'site': 'cor', 'start_date': '2018-10-15'}, {'end_date': '2012-02-07', 'facility': 'M1', 'site': 'gan', 'start_date': '2011-10-09'}, {'end_date': '2016-09-30', 'facility': 'M1', 'site': 'oli', 'start_date': '2015-10-01'}, {'end_date': '2014-03-15', 'facility': 'C1', 'site': 'sgp', 'start_date': '2011-01-18'}, {'end_date': '2014-05-03', 'facility': 'C1', 'site': 'twp', 'start_date': '2011-03-12'}, {'end_date': '2014-05-03', 'facility': 'C3', 'site': 'twp', 'start_date': '2011-01-27'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2014-03-14'\n", + "date_end = '2014-03-15'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['reflectivity_best_estimate', 'reflectivity', 'mean_doppler_velocity']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'reflectivity_best_estimate'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'reflectivity_best_estimate'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/KAZRARSCL/arsclkazrbnd1kollias.c1.ipynb b/VAPs/quicklook/KAZRARSCL/arsclkazrbnd1kollias.c1.ipynb new file mode 100644 index 00000000..6f5afc71 --- /dev/null +++ b/VAPs/quicklook/KAZRARSCL/arsclkazrbnd1kollias.c1.ipynb @@ -0,0 +1,297 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# ARSCLKAZRBND1KOLLIAS.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/kazrarscl) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'arsclkazrbnd1kollias'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2020-05-31', 'facility': 'M1', 'site': 'anx', 'start_date': '2019-12-01'}, {'end_date': '2014-02-07', 'facility': 'C1', 'site': 'nsa', 'start_date': '2011-11-11'}, {'end_date': '2019-04-30', 'facility': 'M1', 'site': 'cor', 'start_date': '2018-10-15'}, {'end_date': '2012-02-07', 'facility': 'M1', 'site': 'gan', 'start_date': '2011-10-09'}, {'end_date': '2014-03-15', 'facility': 'C1', 'site': 'sgp', 'start_date': '2011-01-18'}, {'end_date': '2014-05-03', 'facility': 'C1', 'site': 'twp', 'start_date': '2011-03-12'}, {'end_date': '2014-05-03', 'facility': 'C3', 'site': 'twp', 'start_date': '2011-01-27'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2014-03-14'\n", + "date_end = '2014-03-15'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['cloud_base_best_estimate', 'cloud_layer_base_height', 'cloud_layer_top_height']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'cloud_base_best_estimate'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/KAZRARSCLCLOUDSAT/KAZRARSCLCLOUDSAT_tutorial.ipynb b/VAPs/quicklook/KAZRARSCLCLOUDSAT/KAZRARSCLCLOUDSAT_tutorial.ipynb new file mode 100644 index 00000000..2d5b8bcf --- /dev/null +++ b/VAPs/quicklook/KAZRARSCLCLOUDSAT/KAZRARSCLCLOUDSAT_tutorial.ipynb @@ -0,0 +1,838 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# ARSCLKAZRCLOUDSAT.C1 Notebook\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/kazrarsclcloudsat) for more information about this vap." + ] + }, + { + "cell_type": "markdown", + "id": "97097763", + "metadata": {}, + "source": [ + "In this notebook, we demonstrate the workflow to explore ARM vap data (using arsclkazrcloudsat as an example.) Value-added products (VAPs) are higher-order data products that have been analyzed and processed to ease scientists’ use of ARM data in atmospheric research and global climate models. \n", + "Here is the main content we will cover." + ] + }, + { + "cell_type": "markdown", + "id": "eddec40f", + "metadata": {}, + "source": [ + "# Table of Content\n", + "## Access the data\n", + "* How to retrieve the data\n", + "* Data path and file name conventions\n", + "* Load data\n", + "## Explore the data\n", + "* NetCDF Data structure\n", + "* Xarray essentials\n", + "* Xarray Variable\n", + "## Plot the data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "import random\n", + "\n", + "import glob\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "2372d5be", + "metadata": {}, + "source": [ + "## Access the data" + ] + }, + { + "cell_type": "markdown", + "id": "5b0d9684", + "metadata": {}, + "source": [ + "### How to retrieve the data\n", + "We assume the path \"/data/archive\" is available where you are running this notebook. You can use `os.path.exists(\"/data/archive\")` to verify if the path exists at your machine.\n", + "\n", + "\n", + "### Data path and file name conventions\n", + "There several common terminologies regarding ARM data, for example, data-stream-name, data-level, site, facility, instrument, etc. (For more details, please see [ARM Data File Standards Version 1.2](https://www.google.com/search?q=arm+datastream+facility+cite+definition&rlz=1C1GCEJ_enUS1029US1029&ei=hb41ZICrBPukqtsPvMODIA&ved=0ahUKEwjAgeiS0aL-AhV7kmoFHbzhAAQQ4dUDCBA&uact=5&oq=arm+datastream+facility+cite+definition&gs_lcp=Cgxnd3Mtd2l6LXNlcnAQAzIFCAAQogQyBQgAEKIEMgUIABCiBDIFCAAQogQyBQgAEKIEOgoIABBHENYEELADOgoIIRCgARDDBBAKSgQIQRgAUJ4DWIoOYO0PaAFwAXgAgAGMAYgBuQmSAQMyLjmYAQCgAQHIAQjAAQE&sclient=gws-wiz-serp).) \n", + "\n", + "For example, this notebook is called `arsclkazrcloudsat.c1`, where `arsclkazrcloudsat` is the \"datastream name\", and the `{process.ds_class_level}}` is the \"data level\".\n", + "\n", + "This datastream also contains site `awr` and facility `M1`. (Note: individual datastream might have multiple site-facility pairs.)\n", + "In such a case, the data of this data-stream is stored at `/data/archive/awr/awrarsclkazrcloudsatM1.c1`, which is in the format of `//.`. We can use the following method to assign the data-stream directory `datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )`\n", + "\n", + "The data files under datastream_dir also follows naming conventions. But once reach the datastream_dir level, the most import file naming convention to differentiate the files is \"yyyyMMdd.hhmmss\", which comes handy to filter out files based on datetime. For example, we can use `glob.glob(f'{datastream_dir}/*.200709*.*')` to filter files in 2007 September.\n", + "\n", + "(Note: refer to the https://adc.arm.gov/discovery/#/ and https://adc.arm.gov/solr8/metadata/select API to explore ARM datastream and assoicated available sites and facilities\n", + "\n", + "Please see the following examples in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d7e9eb85", + "metadata": {}, + "outputs": [], + "source": [ + "# Verify if DATA_DIR path exists\n", + "DATA_DIR = \"/data/archive\"\n", + "os.path.exists(DATA_DIR)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "586993fd", + "metadata": {}, + "outputs": [], + "source": [ + "# Speicify datastream_dir following the path conventions and check its existence\n", + "DATASTREAM_NAME = \"arsclkazrcloudsat\"\n", + "DATA_LEVEL = \"c1\"\n", + "site = \"awr\"\n", + "facility = \"M1\"\n", + "datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )\n", + "print(datastream_dir)\n", + "print(os.path.exists(datastream_dir))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0742f7c1", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: list 5 (random) files under datastream_dir\n", + "files = os.listdir(datastream_dir)\n", + "files[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39b98a36", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: get most recent file\n", + "list_of_files = glob.glob(f\"{datastream_dir}/*\") # * means all if need specific format then *.csv\n", + "latest_file = max(list_of_files, key=os.path.getctime)\n", + "latest_file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "902d514e", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus sort datastream files based on datetime\n", + "files = os.listdir(datastream_dir)\n", + "file_sorted = files.copy()\n", + "file_sorted.sort() \n", + "print(file_sorted[:5])\n", + "\n", + "# to reverse\n", + "file_sorted_reverse = files.copy() \n", + "file_sorted_reverse.sort(reverse=True)\n", + "print(file_sorted_reverse[:5])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec5923b2", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: pattern matching\n", + "# filter the 200709** files under datastream_dir\n", + "files_filter = glob.glob(f'{datastream_dir}/*.200709*.*')\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c4a4aa18", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: use armnotebook_utils.file_filter (TODO) to filter files based on datastream info \n" + ] + }, + { + "cell_type": "markdown", + "id": "cfeb9efc", + "metadata": {}, + "source": [ + "### Load data\n", + "The arm data is stored in [NetCDF](https://en.wikipedia.org/wiki/NetCDF#:~:text=%22NetCDF%20(network%20Common%20Data%20Form,format%20for%20representing%20scientific%20data.) format. We can use xarray's `open_dataset` method to load single file, or `open_mfdataset` to open multiple files. (Note: the latter will still return a single xarray dataset object by combining multiple files.)\n", + "\n", + "Note: open_dataset keeps the file handle open and lazy loads its contents. All parameters are passed directly to open_dataset. It is a preferable method over load_dataset for memory-effiency. (more details, please see [xarray.open_dataset](https://docs.xarray.dev/en/stable/generated/xarray.open_dataset.html))\n", + "\n", + "See the following example in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a440a329", + "metadata": {}, + "outputs": [], + "source": [ + "# open single file\n", + "full_path = latest_file\n", + "print(full_path)\n", + "ds_single = xr.open_dataset(full_path)\n", + "ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0143a3d", + "metadata": {}, + "outputs": [], + "source": [ + "print(ds_single)\n", + "print(type(ds_single))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c0f8939", + "metadata": {}, + "outputs": [], + "source": [ + "# open multiple files\n", + "n_files = 3\n", + "full_paths = [os.path.join(datastream_dir, f_path) for f_path in file_sorted_reverse[:n_files]]\n", + "print(full_paths)\n", + "try: # Note: sometimes multiple files cannot be merged, so we used try except here.\n", + " ds_mutiple = xr.open_mfdataset(full_paths)\n", + " # ds_mutiple\n", + " print(type(ds_mutiple))\n", + " print(ds_mutiple)\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "3fa59943", + "metadata": {}, + "source": [ + "## Explore the data" + ] + }, + { + "cell_type": "markdown", + "id": "571f69b9", + "metadata": {}, + "source": [ + "### NetCDF Data structure \n", + "\n", + "\n", + "(If you are confident with NetCDF basics and xarray essentials, feel free to skip this session.)\n", + "Before we dive into data exploration, there are some eseential concpets we should be familiar with (shown below): \n", + "* Dataset\n", + "* Data array\n", + "* Variable\n", + "* Dimenssion\n", + "* Coordinate\n", + "* Data Type\n", + "* Meta Data (Attributes)\n", + "\n", + "We will not go into details about NetCDF basics and here are some references you might find helpful\n", + "* [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#)\n", + "* [Network Common Data Form (NetCDF)](https://www.unidata.ucar.edu/software/netcdf/) and [A Brief History of (netCDF) Time](https://www.unidata.ucar.edu/software/netcdf/time/recs.html)\n", + "\n", + "\n", + "\n", + "### Xarray essentials\n", + "Earlier we introduced the xarray pacakge and used `open_dataset` and `open_mfdataset` to retrieve NetCDF data file as an xarray Dataset object. Recall that you can review the data in a notebook by using `print(ds)` or `ds`. \n", + "\n", + "Using xarray to retrieve the aforementioned NetCDF basics is straightforwared. In general,\n", + "* Dataset: `ds`\n", + "* Data array: `ds.variables`\n", + "* Variable: `ds.ds.variables`\n", + "* Dimenssion: `ds.dims`\n", + "* Coordinate: `ds.coords`\n", + "* Data Type: `type`\n", + "* Meta Data (Attributes)\n", + "\n", + "Also, here are some references if you are new to xarray\n", + "* [Xarray in 45 minutes](https://tutorial.xarray.dev/overview/xarray-in-45-min.html)\n", + "* [Handling NetCDF Files using XArray for Absolute Beginners](https://towardsdatascience.com/handling-netcdf-files-using-xarray-for-absolute-beginners-111a8ab4463f)\n", + "\n", + "Try the following commands in action\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b3eac323", + "metadata": {}, + "outputs": [], + "source": [ + "ds = ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77ecf85d", + "metadata": {}, + "outputs": [], + "source": [ + "# Dataset \n", + "ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25e7de09", + "metadata": {}, + "outputs": [], + "source": [ + "# Data array, Variable\n", + "# Note: the info can be overwhelming. Do not attempt to grasp everything at the first glance.\n", + "ds.variables" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c41b67e", + "metadata": {}, + "outputs": [], + "source": [ + "# dimenssions\n", + "ds.dims" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "156f1dfc", + "metadata": {}, + "outputs": [], + "source": [ + "# coordinates\n", + "ds.coords" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "277d6064", + "metadata": {}, + "outputs": [], + "source": [ + "# Meta Data (Attributes)\n", + "ds.attrs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d334681f", + "metadata": {}, + "outputs": [], + "source": [ + "# type\n", + "print(type(ds))\n", + "print(type(ds.variables))\n", + "print(type(ds.dims))\n", + "print(type(ds.coords))\n", + "print(type(ds.attrs))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "643399d6", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: retrieve variable names only\n", + "list(ds.variables)" + ] + }, + { + "cell_type": "markdown", + "id": "1cf36878", + "metadata": {}, + "source": [ + "#### Discussion: variable vs. coordinates vs. dimenssions. \n", + "From [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#):\n", + "\n", + "* Dimession: A dimension may be used to represent a real physical dimension, for example, time, latitude, longitude, or height. A dimension might also be used to index other quantities, for example station or model-run-number.\n", + "A netCDF dimension has both a name and a length. A dimension length is an arbitrary positive integer, except that one dimension in a netCDF dataset can have the length UNLIMITED.\n", + "\n", + "* Variables: Variables are used to store the bulk of the data in a netCDF dataset. A variable represents an array of values of the same type. A scalar value is treated as a 0-dimensional array. A variable has a name, a data type, and a shape described by its list of dimensions specified when the variable is created. A variable may also have associated attributes, which may be added, deleted or changed after the variable is created.\n", + "\n", + "* Coordinate (Variables): It is legal for a variable to have the same name as a dimension. Such variables have no special meaning to the netCDF library. However there is a convention that such variables should be treated in a special way by software using this library.\n", + "A variable with the same name as a dimension is called a coordinate variable. It typically defines a physical coordinate corresponding to that dimension.\n", + "\n", + "\n", + "Tips: By definition, coordinates and dimenssions are also variable. For example ds.time is a coordinate but it is also a special variable. But in practice, when we talk about variable, it implies regular/non-coordinate variable. We will use this convention for the remaining notebook. " + ] + }, + { + "cell_type": "markdown", + "id": "d47120f9", + "metadata": {}, + "source": [ + "### Xarray Variable\n", + "\n", + "\n", + "We can use `ds.variables` to access varialbes of a dataset. But many times viewing print-out the whole datasets can be overwhemling. Instead, we would work on the individual variable (also called Xarray data array.) using the `ds[var_name]` syntax, where `var_name` is the name of the variable. See the following examples in action.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5a57e136", + "metadata": {}, + "outputs": [], + "source": [ + "print(type(ds[\"time\"]))\n", + "ds[\"time\"]" + ] + }, + { + "cell_type": "markdown", + "id": "51ec643e", + "metadata": {}, + "source": [ + "#### Variable properties\n", + "For individual varible, the following properties are mostly used with the assocaited xarray method to retrieve them. (assuming var = ds[var_name])\n", + "* name: `var.name`\n", + "* data content: `var.data`\n", + "* attributes: `var.attrs`\n", + "* dimenstions: `var.dims`\n", + "* data type: `var.data.dtype`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a632a525", + "metadata": {}, + "outputs": [], + "source": [ + "var_name = \"time\"\n", + "var = ds[var_name]\n", + "\n", + "print(\"var.name: \\n\", var.name, \"\\n\")\n", + "print(\"var.data: \\n\", var.data, \"\\n\")\n", + "print(\"var.attrs: \\n\", var.attrs, \"\\n\")\n", + "print(\"var.dims: \\n\", var.dims, \"\\n\")\n", + "print(\"var.data.dtype: \\n\", var.data.dtype, \"\\n\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b256b07f", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: indivdual variable info table (collect and display variable info as a tabular format)\n", + "df_info = pd.DataFrame()\n", + "df_info[\"var_name\"] = list(ds.variables)\n", + "df_info[\"dims\"] = df_info.var_name.apply(lambda x: ds[x].dims)\n", + "df_info[\"is_dim\"] = df_info.var_name.apply(lambda x: x in ds.dims)\n", + "df_info[\"n_dim\"] = df_info.var_name.apply(lambda x: len(ds[x].dims))\n", + "df_info[\"attrs\"] = df_info.var_name.apply(lambda x: ds[x].attrs)\n", + "df_info[\"dtype\"] = df_info.var_name.apply(lambda x: ds[x].data.dtype)\n", + "df_info" + ] + }, + { + "cell_type": "markdown", + "id": "e6a36b96", + "metadata": {}, + "source": [ + "### Data cleaning/Preprocessing (skipped)\n", + "Data cleaning and preprocessing is an important stage in the the data analysis pipeline. However it will be out of the scope of this notebook. For more information about data cleaning and preprocessing basics, here are some references. \n", + "* [Xarray Fundamentals](https://earth-env-data-science.github.io/lectures/xarray/xarray.html)\n", + "* [Xarray Tutorial — Pangeo Gallery documentation](http://gallery.pangeo.io/repos/pangeo-data/pangeo-tutorial-gallery/xarray.html)\n", + "* [Pythonic Data Cleaning With pandas and NumPy](https://realpython.com/python-data-cleaning-numpy-pandas/)\n", + "* [Pandas - Cleaning Data](https://www.w3schools.com/python/pandas/pandas_cleaning.asp)" + ] + }, + { + "cell_type": "markdown", + "id": "ae6e1a69", + "metadata": {}, + "source": [ + "### Plotting\n", + "Data visualization (or plotting) is another important data analysis topic and deserves its own discussion. Here in this notebook we will only demonstrate basic tool for simple data visualization tasks.\n", + "\n", + "Here are some reference you might find useful:\n", + "* xarray plotting: https://docs.xarray.dev/en/stable/user-guide/plotting.html\n", + "* Atmospheric Community Toolkit (ACT): https://arm-doe.github.io/ACT/index.html\n", + "\n", + "Note: this notebook is auto-generated using a template. It uses a general idea to select variable(s) to plot and is not customized to each indivdual notebook. Feel free to change the variables in intrrests, especially certain figure is failed to plot." + ] + }, + { + "cell_type": "markdown", + "id": "7e417fb2", + "metadata": {}, + "source": [ + "#### 1-dimenssional basic time series plot\n", + "\n", + "For the following plot we would like to find variables such that\n", + "* it has one and only one dimession\n", + "* \"time\" is its coordinate variable\n", + "* it is not a dimenssion itself,\n", + "* it is not a special variable with substrings within [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "\n", + "Please see the following example in action. (For more details about pandas filtering, please see the following references.)\n", + "* [pandas: multiple conditions while indexing data frame](https://stackoverflow.com/questions/22591174/pandas-multiple-conditions-while-indexing-data-frame-unexpected-behavior)\n", + "* [pandas.DataFrame.apply](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.apply.html)\n", + "* [How to test if a string contains one of the substrings in a list...](https://stackoverflow.com/questions/26577516/how-to-test-if-a-string-contains-one-of-the-substrings-in-a-list-in-pandas" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30edac2d", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==1, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter = df_info[(df_info.n_dim == 1) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4777a659", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_1d = df_filter.var_name.values[0]\n", + " var_1d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb5ae985", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " ds[var_1d].plot()\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "9e41066e", + "metadata": {}, + "source": [ + "#### 2-dimenssional basic plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09bd4adc", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==2, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_2 = df_info[(df_info.n_dim == 2) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2cdea1a9", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_2d = df_filter_2.var_name.values[0]\n", + " var_2d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5f48879", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " print(ds[var_2d].dims)\n", + " # ds[var_2d].plot()\n", + "\n", + " # conventionally, use \"time\" as x-axis\n", + " ds[var_2d].plot(x=\"time\")\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "7554b2a6", + "metadata": {}, + "source": [ + "#### qc-plotting (optional)\n", + "\n", + "Note: act qc plotting has more strict requirement, one of them is the associated qc variable need to have \"flag_masks\" attributes. Which is added by using ds = act.io.armfiles.read_netcdf(files_list), then ds.clean.cleanup()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71c2096f", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " ds_act = act.io.armfiles.read_netcdf(full_path)\n", + " print(type(ds_act))\n", + " ds_act.clean.cleanup()\n", + "\n", + " # or \n", + " # ds.clean.cleanup()\n", + "except Exception as e:\n", + " print(\"ERROR\", e)\n", + " ds_act = ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "844f8505", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter valid variables for ACT qc plotting\n", + "condition = (\"qc_\" + df_info.var_name).apply(lambda x: ds_act[x].attrs.get(\"flag_masks\") is not None \n", + " if x in list(ds.variables) else False)\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_3 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings))) &\n", + " condition\n", + " ]\n", + "df_filter_3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd38cc6", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "try:\n", + " qc_variable = df_filter_3.var_name.values[0]\n", + " print(qc_variable)\n", + "except Exception as e:\n", + " print(e)\n", + " \n", + "try:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + " qc_ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "d76e4d27", + "metadata": {}, + "source": [ + "#### bonus: choose variables to plot from a dropdown menu " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb733804", + "metadata": {}, + "outputs": [], + "source": [ + "# Valid variables filtering\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_4 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82d5c20d", + "metadata": {}, + "outputs": [], + "source": [ + "# example 1: using xarray plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2fc9aaa4", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# fig, ax = plt.subplots(figsize=(10, 4))\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "# fig.clear() # Remove old lines from plot and plot new one\n", + "# if len(ds[var].dims)==2:\n", + "# ds[var].plot(x=\"time\", add_colorbar=False)\n", + "# else:\n", + "# ds[var].plot()\n", + "# plt.grid()\n", + "# plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fabdd802", + "metadata": {}, + "outputs": [], + "source": [ + "# example 2: using act plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "962b4186", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "\n", + "\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "\n", + "# i_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + "# i_display.add_subplots((1,), figsize=(10, 4))\n", + "# ax = i_display.plot(var, subplot_index=(0,), set_title=f\"{var} ({ds_act[var].attrs['long_name']})\",)\n", + "\n", + "# ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "# ax.grid()\n", + "# plt.show()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/KAZRARSCLCLOUDSAT/arsclkazrcloudsat.c1.ipynb b/VAPs/quicklook/KAZRARSCLCLOUDSAT/arsclkazrcloudsat.c1.ipynb new file mode 100644 index 00000000..6db5a260 --- /dev/null +++ b/VAPs/quicklook/KAZRARSCLCLOUDSAT/arsclkazrcloudsat.c1.ipynb @@ -0,0 +1,339 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# ARSCLKAZRCLOUDSAT.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/kazrarsclcloudsat) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'arsclkazrcloudsat'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2016-12-31', 'facility': 'M1', 'site': 'awr', 'start_date': '2016-04-01'}, {'end_date': '2017-06-30', 'facility': 'C1', 'site': 'nsa', 'start_date': '2012-03-01'}, {'end_date': '2017-11-29', 'facility': 'M1', 'site': 'oli', 'start_date': '2015-11-01'}, {'end_date': '2017-08-31', 'facility': 'C1', 'site': 'sgp', 'start_date': '2012-03-01'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2017-08-30'\n", + "date_end = '2017-08-31'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['reflectivity_best_estimate', 'reflectivity', 'mean_doppler_velocity']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'reflectivity_best_estimate'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'reflectivity_best_estimate'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/KAZRCFRCOR/KAZRCFRCOR_tutorial.ipynb b/VAPs/quicklook/KAZRCFRCOR/KAZRCFRCOR_tutorial.ipynb new file mode 100644 index 00000000..fec24d1b --- /dev/null +++ b/VAPs/quicklook/KAZRCFRCOR/KAZRCFRCOR_tutorial.ipynb @@ -0,0 +1,838 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# KAZRCFRCORGE.C1 Notebook\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/kazrcfrcor) for more information about this vap." + ] + }, + { + "cell_type": "markdown", + "id": "97097763", + "metadata": {}, + "source": [ + "In this notebook, we demonstrate the workflow to explore ARM vap data (using kazrcfrcorge as an example.) Value-added products (VAPs) are higher-order data products that have been analyzed and processed to ease scientists’ use of ARM data in atmospheric research and global climate models. \n", + "Here is the main content we will cover." + ] + }, + { + "cell_type": "markdown", + "id": "eddec40f", + "metadata": {}, + "source": [ + "# Table of Content\n", + "## Access the data\n", + "* How to retrieve the data\n", + "* Data path and file name conventions\n", + "* Load data\n", + "## Explore the data\n", + "* NetCDF Data structure\n", + "* Xarray essentials\n", + "* Xarray Variable\n", + "## Plot the data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "import random\n", + "\n", + "import glob\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "2372d5be", + "metadata": {}, + "source": [ + "## Access the data" + ] + }, + { + "cell_type": "markdown", + "id": "5b0d9684", + "metadata": {}, + "source": [ + "### How to retrieve the data\n", + "We assume the path \"/data/archive\" is available where you are running this notebook. You can use `os.path.exists(\"/data/archive\")` to verify if the path exists at your machine.\n", + "\n", + "\n", + "### Data path and file name conventions\n", + "There several common terminologies regarding ARM data, for example, data-stream-name, data-level, site, facility, instrument, etc. (For more details, please see [ARM Data File Standards Version 1.2](https://www.google.com/search?q=arm+datastream+facility+cite+definition&rlz=1C1GCEJ_enUS1029US1029&ei=hb41ZICrBPukqtsPvMODIA&ved=0ahUKEwjAgeiS0aL-AhV7kmoFHbzhAAQQ4dUDCBA&uact=5&oq=arm+datastream+facility+cite+definition&gs_lcp=Cgxnd3Mtd2l6LXNlcnAQAzIFCAAQogQyBQgAEKIEMgUIABCiBDIFCAAQogQyBQgAEKIEOgoIABBHENYEELADOgoIIRCgARDDBBAKSgQIQRgAUJ4DWIoOYO0PaAFwAXgAgAGMAYgBuQmSAQMyLjmYAQCgAQHIAQjAAQE&sclient=gws-wiz-serp).) \n", + "\n", + "For example, this notebook is called `kazrcfrcorge.c1`, where `kazrcfrcorge` is the \"datastream name\", and the `{process.ds_class_level}}` is the \"data level\".\n", + "\n", + "This datastream also contains site `cor` and facility `M1`. (Note: individual datastream might have multiple site-facility pairs.)\n", + "In such a case, the data of this data-stream is stored at `/data/archive/cor/corkazrcfrcorgeM1.c1`, which is in the format of `//.`. We can use the following method to assign the data-stream directory `datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )`\n", + "\n", + "The data files under datastream_dir also follows naming conventions. But once reach the datastream_dir level, the most import file naming convention to differentiate the files is \"yyyyMMdd.hhmmss\", which comes handy to filter out files based on datetime. For example, we can use `glob.glob(f'{datastream_dir}/*.200709*.*')` to filter files in 2007 September.\n", + "\n", + "(Note: refer to the https://adc.arm.gov/discovery/#/ and https://adc.arm.gov/solr8/metadata/select API to explore ARM datastream and assoicated available sites and facilities\n", + "\n", + "Please see the following examples in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d7e9eb85", + "metadata": {}, + "outputs": [], + "source": [ + "# Verify if DATA_DIR path exists\n", + "DATA_DIR = \"/data/archive\"\n", + "os.path.exists(DATA_DIR)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "586993fd", + "metadata": {}, + "outputs": [], + "source": [ + "# Speicify datastream_dir following the path conventions and check its existence\n", + "DATASTREAM_NAME = \"kazrcfrcorge\"\n", + "DATA_LEVEL = \"c1\"\n", + "site = \"cor\"\n", + "facility = \"M1\"\n", + "datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )\n", + "print(datastream_dir)\n", + "print(os.path.exists(datastream_dir))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0742f7c1", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: list 5 (random) files under datastream_dir\n", + "files = os.listdir(datastream_dir)\n", + "files[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39b98a36", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: get most recent file\n", + "list_of_files = glob.glob(f\"{datastream_dir}/*\") # * means all if need specific format then *.csv\n", + "latest_file = max(list_of_files, key=os.path.getctime)\n", + "latest_file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "902d514e", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus sort datastream files based on datetime\n", + "files = os.listdir(datastream_dir)\n", + "file_sorted = files.copy()\n", + "file_sorted.sort() \n", + "print(file_sorted[:5])\n", + "\n", + "# to reverse\n", + "file_sorted_reverse = files.copy() \n", + "file_sorted_reverse.sort(reverse=True)\n", + "print(file_sorted_reverse[:5])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec5923b2", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: pattern matching\n", + "# filter the 200709** files under datastream_dir\n", + "files_filter = glob.glob(f'{datastream_dir}/*.200709*.*')\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c4a4aa18", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: use armnotebook_utils.file_filter (TODO) to filter files based on datastream info \n" + ] + }, + { + "cell_type": "markdown", + "id": "cfeb9efc", + "metadata": {}, + "source": [ + "### Load data\n", + "The arm data is stored in [NetCDF](https://en.wikipedia.org/wiki/NetCDF#:~:text=%22NetCDF%20(network%20Common%20Data%20Form,format%20for%20representing%20scientific%20data.) format. We can use xarray's `open_dataset` method to load single file, or `open_mfdataset` to open multiple files. (Note: the latter will still return a single xarray dataset object by combining multiple files.)\n", + "\n", + "Note: open_dataset keeps the file handle open and lazy loads its contents. All parameters are passed directly to open_dataset. It is a preferable method over load_dataset for memory-effiency. (more details, please see [xarray.open_dataset](https://docs.xarray.dev/en/stable/generated/xarray.open_dataset.html))\n", + "\n", + "See the following example in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a440a329", + "metadata": {}, + "outputs": [], + "source": [ + "# open single file\n", + "full_path = latest_file\n", + "print(full_path)\n", + "ds_single = xr.open_dataset(full_path)\n", + "ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0143a3d", + "metadata": {}, + "outputs": [], + "source": [ + "print(ds_single)\n", + "print(type(ds_single))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c0f8939", + "metadata": {}, + "outputs": [], + "source": [ + "# open multiple files\n", + "n_files = 3\n", + "full_paths = [os.path.join(datastream_dir, f_path) for f_path in file_sorted_reverse[:n_files]]\n", + "print(full_paths)\n", + "try: # Note: sometimes multiple files cannot be merged, so we used try except here.\n", + " ds_mutiple = xr.open_mfdataset(full_paths)\n", + " # ds_mutiple\n", + " print(type(ds_mutiple))\n", + " print(ds_mutiple)\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "3fa59943", + "metadata": {}, + "source": [ + "## Explore the data" + ] + }, + { + "cell_type": "markdown", + "id": "571f69b9", + "metadata": {}, + "source": [ + "### NetCDF Data structure \n", + "\n", + "\n", + "(If you are confident with NetCDF basics and xarray essentials, feel free to skip this session.)\n", + "Before we dive into data exploration, there are some eseential concpets we should be familiar with (shown below): \n", + "* Dataset\n", + "* Data array\n", + "* Variable\n", + "* Dimenssion\n", + "* Coordinate\n", + "* Data Type\n", + "* Meta Data (Attributes)\n", + "\n", + "We will not go into details about NetCDF basics and here are some references you might find helpful\n", + "* [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#)\n", + "* [Network Common Data Form (NetCDF)](https://www.unidata.ucar.edu/software/netcdf/) and [A Brief History of (netCDF) Time](https://www.unidata.ucar.edu/software/netcdf/time/recs.html)\n", + "\n", + "\n", + "\n", + "### Xarray essentials\n", + "Earlier we introduced the xarray pacakge and used `open_dataset` and `open_mfdataset` to retrieve NetCDF data file as an xarray Dataset object. Recall that you can review the data in a notebook by using `print(ds)` or `ds`. \n", + "\n", + "Using xarray to retrieve the aforementioned NetCDF basics is straightforwared. In general,\n", + "* Dataset: `ds`\n", + "* Data array: `ds.variables`\n", + "* Variable: `ds.ds.variables`\n", + "* Dimenssion: `ds.dims`\n", + "* Coordinate: `ds.coords`\n", + "* Data Type: `type`\n", + "* Meta Data (Attributes)\n", + "\n", + "Also, here are some references if you are new to xarray\n", + "* [Xarray in 45 minutes](https://tutorial.xarray.dev/overview/xarray-in-45-min.html)\n", + "* [Handling NetCDF Files using XArray for Absolute Beginners](https://towardsdatascience.com/handling-netcdf-files-using-xarray-for-absolute-beginners-111a8ab4463f)\n", + "\n", + "Try the following commands in action\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b3eac323", + "metadata": {}, + "outputs": [], + "source": [ + "ds = ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77ecf85d", + "metadata": {}, + "outputs": [], + "source": [ + "# Dataset \n", + "ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25e7de09", + "metadata": {}, + "outputs": [], + "source": [ + "# Data array, Variable\n", + "# Note: the info can be overwhelming. Do not attempt to grasp everything at the first glance.\n", + "ds.variables" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c41b67e", + "metadata": {}, + "outputs": [], + "source": [ + "# dimenssions\n", + "ds.dims" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "156f1dfc", + "metadata": {}, + "outputs": [], + "source": [ + "# coordinates\n", + "ds.coords" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "277d6064", + "metadata": {}, + "outputs": [], + "source": [ + "# Meta Data (Attributes)\n", + "ds.attrs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d334681f", + "metadata": {}, + "outputs": [], + "source": [ + "# type\n", + "print(type(ds))\n", + "print(type(ds.variables))\n", + "print(type(ds.dims))\n", + "print(type(ds.coords))\n", + "print(type(ds.attrs))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "643399d6", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: retrieve variable names only\n", + "list(ds.variables)" + ] + }, + { + "cell_type": "markdown", + "id": "1cf36878", + "metadata": {}, + "source": [ + "#### Discussion: variable vs. coordinates vs. dimenssions. \n", + "From [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#):\n", + "\n", + "* Dimession: A dimension may be used to represent a real physical dimension, for example, time, latitude, longitude, or height. A dimension might also be used to index other quantities, for example station or model-run-number.\n", + "A netCDF dimension has both a name and a length. A dimension length is an arbitrary positive integer, except that one dimension in a netCDF dataset can have the length UNLIMITED.\n", + "\n", + "* Variables: Variables are used to store the bulk of the data in a netCDF dataset. A variable represents an array of values of the same type. A scalar value is treated as a 0-dimensional array. A variable has a name, a data type, and a shape described by its list of dimensions specified when the variable is created. A variable may also have associated attributes, which may be added, deleted or changed after the variable is created.\n", + "\n", + "* Coordinate (Variables): It is legal for a variable to have the same name as a dimension. Such variables have no special meaning to the netCDF library. However there is a convention that such variables should be treated in a special way by software using this library.\n", + "A variable with the same name as a dimension is called a coordinate variable. It typically defines a physical coordinate corresponding to that dimension.\n", + "\n", + "\n", + "Tips: By definition, coordinates and dimenssions are also variable. For example ds.time is a coordinate but it is also a special variable. But in practice, when we talk about variable, it implies regular/non-coordinate variable. We will use this convention for the remaining notebook. " + ] + }, + { + "cell_type": "markdown", + "id": "d47120f9", + "metadata": {}, + "source": [ + "### Xarray Variable\n", + "\n", + "\n", + "We can use `ds.variables` to access varialbes of a dataset. But many times viewing print-out the whole datasets can be overwhemling. Instead, we would work on the individual variable (also called Xarray data array.) using the `ds[var_name]` syntax, where `var_name` is the name of the variable. See the following examples in action.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5a57e136", + "metadata": {}, + "outputs": [], + "source": [ + "print(type(ds[\"time\"]))\n", + "ds[\"time\"]" + ] + }, + { + "cell_type": "markdown", + "id": "51ec643e", + "metadata": {}, + "source": [ + "#### Variable properties\n", + "For individual varible, the following properties are mostly used with the assocaited xarray method to retrieve them. (assuming var = ds[var_name])\n", + "* name: `var.name`\n", + "* data content: `var.data`\n", + "* attributes: `var.attrs`\n", + "* dimenstions: `var.dims`\n", + "* data type: `var.data.dtype`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a632a525", + "metadata": {}, + "outputs": [], + "source": [ + "var_name = \"time\"\n", + "var = ds[var_name]\n", + "\n", + "print(\"var.name: \\n\", var.name, \"\\n\")\n", + "print(\"var.data: \\n\", var.data, \"\\n\")\n", + "print(\"var.attrs: \\n\", var.attrs, \"\\n\")\n", + "print(\"var.dims: \\n\", var.dims, \"\\n\")\n", + "print(\"var.data.dtype: \\n\", var.data.dtype, \"\\n\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b256b07f", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: indivdual variable info table (collect and display variable info as a tabular format)\n", + "df_info = pd.DataFrame()\n", + "df_info[\"var_name\"] = list(ds.variables)\n", + "df_info[\"dims\"] = df_info.var_name.apply(lambda x: ds[x].dims)\n", + "df_info[\"is_dim\"] = df_info.var_name.apply(lambda x: x in ds.dims)\n", + "df_info[\"n_dim\"] = df_info.var_name.apply(lambda x: len(ds[x].dims))\n", + "df_info[\"attrs\"] = df_info.var_name.apply(lambda x: ds[x].attrs)\n", + "df_info[\"dtype\"] = df_info.var_name.apply(lambda x: ds[x].data.dtype)\n", + "df_info" + ] + }, + { + "cell_type": "markdown", + "id": "e6a36b96", + "metadata": {}, + "source": [ + "### Data cleaning/Preprocessing (skipped)\n", + "Data cleaning and preprocessing is an important stage in the the data analysis pipeline. However it will be out of the scope of this notebook. For more information about data cleaning and preprocessing basics, here are some references. \n", + "* [Xarray Fundamentals](https://earth-env-data-science.github.io/lectures/xarray/xarray.html)\n", + "* [Xarray Tutorial — Pangeo Gallery documentation](http://gallery.pangeo.io/repos/pangeo-data/pangeo-tutorial-gallery/xarray.html)\n", + "* [Pythonic Data Cleaning With pandas and NumPy](https://realpython.com/python-data-cleaning-numpy-pandas/)\n", + "* [Pandas - Cleaning Data](https://www.w3schools.com/python/pandas/pandas_cleaning.asp)" + ] + }, + { + "cell_type": "markdown", + "id": "ae6e1a69", + "metadata": {}, + "source": [ + "### Plotting\n", + "Data visualization (or plotting) is another important data analysis topic and deserves its own discussion. Here in this notebook we will only demonstrate basic tool for simple data visualization tasks.\n", + "\n", + "Here are some reference you might find useful:\n", + "* xarray plotting: https://docs.xarray.dev/en/stable/user-guide/plotting.html\n", + "* Atmospheric Community Toolkit (ACT): https://arm-doe.github.io/ACT/index.html\n", + "\n", + "Note: this notebook is auto-generated using a template. It uses a general idea to select variable(s) to plot and is not customized to each indivdual notebook. Feel free to change the variables in intrrests, especially certain figure is failed to plot." + ] + }, + { + "cell_type": "markdown", + "id": "7e417fb2", + "metadata": {}, + "source": [ + "#### 1-dimenssional basic time series plot\n", + "\n", + "For the following plot we would like to find variables such that\n", + "* it has one and only one dimession\n", + "* \"time\" is its coordinate variable\n", + "* it is not a dimenssion itself,\n", + "* it is not a special variable with substrings within [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "\n", + "Please see the following example in action. (For more details about pandas filtering, please see the following references.)\n", + "* [pandas: multiple conditions while indexing data frame](https://stackoverflow.com/questions/22591174/pandas-multiple-conditions-while-indexing-data-frame-unexpected-behavior)\n", + "* [pandas.DataFrame.apply](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.apply.html)\n", + "* [How to test if a string contains one of the substrings in a list...](https://stackoverflow.com/questions/26577516/how-to-test-if-a-string-contains-one-of-the-substrings-in-a-list-in-pandas" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30edac2d", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==1, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter = df_info[(df_info.n_dim == 1) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4777a659", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_1d = df_filter.var_name.values[0]\n", + " var_1d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb5ae985", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " ds[var_1d].plot()\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "9e41066e", + "metadata": {}, + "source": [ + "#### 2-dimenssional basic plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09bd4adc", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==2, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_2 = df_info[(df_info.n_dim == 2) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2cdea1a9", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_2d = df_filter_2.var_name.values[0]\n", + " var_2d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5f48879", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " print(ds[var_2d].dims)\n", + " # ds[var_2d].plot()\n", + "\n", + " # conventionally, use \"time\" as x-axis\n", + " ds[var_2d].plot(x=\"time\")\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "7554b2a6", + "metadata": {}, + "source": [ + "#### qc-plotting (optional)\n", + "\n", + "Note: act qc plotting has more strict requirement, one of them is the associated qc variable need to have \"flag_masks\" attributes. Which is added by using ds = act.io.armfiles.read_netcdf(files_list), then ds.clean.cleanup()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71c2096f", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " ds_act = act.io.armfiles.read_netcdf(full_path)\n", + " print(type(ds_act))\n", + " ds_act.clean.cleanup()\n", + "\n", + " # or \n", + " # ds.clean.cleanup()\n", + "except Exception as e:\n", + " print(\"ERROR\", e)\n", + " ds_act = ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "844f8505", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter valid variables for ACT qc plotting\n", + "condition = (\"qc_\" + df_info.var_name).apply(lambda x: ds_act[x].attrs.get(\"flag_masks\") is not None \n", + " if x in list(ds.variables) else False)\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_3 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings))) &\n", + " condition\n", + " ]\n", + "df_filter_3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd38cc6", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "try:\n", + " qc_variable = df_filter_3.var_name.values[0]\n", + " print(qc_variable)\n", + "except Exception as e:\n", + " print(e)\n", + " \n", + "try:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + " qc_ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "d76e4d27", + "metadata": {}, + "source": [ + "#### bonus: choose variables to plot from a dropdown menu " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb733804", + "metadata": {}, + "outputs": [], + "source": [ + "# Valid variables filtering\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_4 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82d5c20d", + "metadata": {}, + "outputs": [], + "source": [ + "# example 1: using xarray plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2fc9aaa4", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# fig, ax = plt.subplots(figsize=(10, 4))\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "# fig.clear() # Remove old lines from plot and plot new one\n", + "# if len(ds[var].dims)==2:\n", + "# ds[var].plot(x=\"time\", add_colorbar=False)\n", + "# else:\n", + "# ds[var].plot()\n", + "# plt.grid()\n", + "# plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fabdd802", + "metadata": {}, + "outputs": [], + "source": [ + "# example 2: using act plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "962b4186", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "\n", + "\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "\n", + "# i_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + "# i_display.add_subplots((1,), figsize=(10, 4))\n", + "# ax = i_display.plot(var, subplot_index=(0,), set_title=f\"{var} ({ds_act[var].attrs['long_name']})\",)\n", + "\n", + "# ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "# ax.grid()\n", + "# plt.show()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/KAZRCFRCOR/kazrcfrcorge.c1.ipynb b/VAPs/quicklook/KAZRCFRCOR/kazrcfrcorge.c1.ipynb new file mode 100644 index 00000000..211aad8d --- /dev/null +++ b/VAPs/quicklook/KAZRCFRCOR/kazrcfrcorge.c1.ipynb @@ -0,0 +1,339 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# KAZRCFRCORGE.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/kazrcfrcor) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'kazrcfrcorge'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2019-04-30', 'facility': 'M1', 'site': 'cor', 'start_date': '2018-10-15'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'cor', 'M1' )\n", + "\n", + "date_start = '2019-04-29'\n", + "date_end = '2019-04-30'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['linear_depolarization_ratio', 'mean_doppler_velocity', 'mean_doppler_velocity_crosspolar_v']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'mean_doppler_velocity'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'linear_depolarization_ratio'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/KAZRCFRCOR/kazrcfrcormd.c1.ipynb b/VAPs/quicklook/KAZRCFRCOR/kazrcfrcormd.c1.ipynb new file mode 100644 index 00000000..9bf125ae --- /dev/null +++ b/VAPs/quicklook/KAZRCFRCOR/kazrcfrcormd.c1.ipynb @@ -0,0 +1,339 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# KAZRCFRCORMD.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/kazrcfrcor) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'kazrcfrcormd'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2019-04-30', 'facility': 'M1', 'site': 'cor', 'start_date': '2018-10-15'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'cor', 'M1' )\n", + "\n", + "date_start = '2019-04-29'\n", + "date_end = '2019-04-30'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['linear_depolarization_ratio', 'mean_doppler_velocity', 'mean_doppler_velocity_crosspolar_v']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'mean_doppler_velocity'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'linear_depolarization_ratio'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/KAZRCOR/.ipynb_checkpoints/kazrcorge.c1-checkpoint.ipynb b/VAPs/quicklook/KAZRCOR/.ipynb_checkpoints/kazrcorge.c1-checkpoint.ipynb new file mode 100644 index 00000000..bf322ff2 --- /dev/null +++ b/VAPs/quicklook/KAZRCOR/.ipynb_checkpoints/kazrcorge.c1-checkpoint.ipynb @@ -0,0 +1,445 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# KAZRCORGE.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/kazrcor) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'kazrcorge'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2014-02-07', 'facility': 'C1', 'site': 'nsa', 'start_date': '2011-11-11'}, {'end_date': '2014-03-15', 'facility': 'C1', 'site': 'sgp', 'start_date': '2011-01-18'}, {'end_date': '2014-03-16', 'facility': 'C1', 'site': 'twp', 'start_date': '2011-03-12'}, {'end_date': '2014-05-03', 'facility': 'C3', 'site': 'twp', 'start_date': '2011-01-27'}]" + ] + }, + { + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "ac6764f5", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The following locations and date ranges are available for this VAP:\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
sitefacilitystart_dateend_date
0nsaC12011-11-112014-02-07
1sgpC12011-01-182014-03-15
2twpC12011-03-122014-03-16
3twpC32011-01-272014-05-03
\n", + "
" + ], + "text/plain": [ + " site facility start_date end_date\n", + "0 nsa C1 2011-11-11 2014-02-07\n", + "1 sgp C1 2011-01-18 2014-03-15\n", + "2 twp C1 2011-03-12 2014-03-16\n", + "3 twp C3 2011-01-27 2014-05-03" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2014-03-14'\n", + "date_end = '2014-03-15'" + ] + }, + { + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'/data/archive/sgp/sgpkazrcorgeC1.c1'" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['20140314', '20140315']" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "51feea2e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['/data/archive/sgp/sgpkazrcorgeC1.c1/sgpkazrcorgeC1.c1.20140314.000001.nc',\n", + " '/data/archive/sgp/sgpkazrcorgeC1.c1/sgpkazrcorgeC1.c1.20140315.000002.nc']" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['reflectivity', 'mean_doppler_velocity', 'spectral_width']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'reflectivity'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'reflectivity'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/KAZRCOR/.ipynb_checkpoints/kazrcorhi.c1-checkpoint.ipynb b/VAPs/quicklook/KAZRCOR/.ipynb_checkpoints/kazrcorhi.c1-checkpoint.ipynb new file mode 100644 index 00000000..5595ed42 --- /dev/null +++ b/VAPs/quicklook/KAZRCOR/.ipynb_checkpoints/kazrcorhi.c1-checkpoint.ipynb @@ -0,0 +1,1856 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# KAZRCORHI.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/kazrcor) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'kazrcorhi'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2014-03-16', 'facility': 'C1', 'site': 'twp', 'start_date': '2011-03-12'}, {'end_date': '2014-05-03', 'facility': 'C3', 'site': 'twp', 'start_date': '2011-01-27'}]" + ] + }, + { + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "ac6764f5", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The following locations and date ranges are available for this VAP:\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
sitefacilitystart_dateend_date
0twpC12011-03-122014-03-16
1twpC32011-01-272014-05-03
\n", + "
" + ], + "text/plain": [ + " site facility start_date end_date\n", + "0 twp C1 2011-03-12 2014-03-16\n", + "1 twp C3 2011-01-27 2014-05-03" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'twp', 'C1' )\n", + "\n", + "date_start = '2014-03-15'\n", + "date_end = '2014-03-16'" + ] + }, + { + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'/data/archive/twp/twpkazrcorhiC1.c1'" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['20140315', '20140316']" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "51feea2e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['/data/archive/twp/twpkazrcorhiC1.c1/twpkazrcorhiC1.c1.20140315.000001.nc',\n", + " '/data/archive/twp/twpkazrcorhiC1.c1/twpkazrcorhiC1.c1.20140316.000001.nc']" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2 files loaded\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
<xarray.Dataset>\n",
+       "Dimensions:                                   (time: 89645, range: 516)\n",
+       "Coordinates:\n",
+       "  * time                                      (time) datetime64[ns] 2014-03-1...\n",
+       "  * range                                     (range) float32 2.007e+03 ... 1...\n",
+       "Data variables: (12/23)\n",
+       "    base_time                                 (time) datetime64[ns] 2014-03-1...\n",
+       "    time_offset                               (time) datetime64[ns] 2014-03-1...\n",
+       "    reflectivity_copol                        (time, range) float32 dask.array<chunksize=(46771, 516), meta=np.ndarray>\n",
+       "    qc_reflectivity_copol                     (time, range) int32 dask.array<chunksize=(46771, 516), meta=np.ndarray>\n",
+       "    gaseous_attenuation_correction_copol      (time, range) float32 dask.array<chunksize=(46771, 516), meta=np.ndarray>\n",
+       "    qc_gaseous_attenuation_correction_copol   (time, range) int32 dask.array<chunksize=(46771, 516), meta=np.ndarray>\n",
+       "    ...                                        ...\n",
+       "    qc_rh                                     (time, range) int32 dask.array<chunksize=(46771, 516), meta=np.ndarray>\n",
+       "    bar_pres                                  (time, range) float32 dask.array<chunksize=(46771, 516), meta=np.ndarray>\n",
+       "    qc_bar_pres                               (time, range) int32 dask.array<chunksize=(46771, 516), meta=np.ndarray>\n",
+       "    lat                                       (time) float32 -2.06 ... -2.06\n",
+       "    lon                                       (time) float32 147.4 ... 147.4\n",
+       "    alt                                       (time) float32 4.0 4.0 ... 4.0 4.0\n",
+       "Attributes: (12/32)\n",
+       "    command_line:                idl -R -n kazrcor -s twp -f C1 -b 20140315 -...\n",
+       "    Conventions:                 ARM-1.1\n",
+       "    process_version:             vap-kazrcor-1.6-0.el6\n",
+       "    input_datastreams:           twpkazrgeC1.b1 : 1.3 : 20140315.000001\\ntwpk...\n",
+       "    dod_version:                 kazrcorhi-c1-1.3\n",
+       "    site_id:                     twp\n",
+       "    ...                          ...\n",
+       "    doi:                         10.5439/1228772\n",
+       "    history:                     created by user ttoto on machine chalk at 20...\n",
+       "    _file_dates:                 ['20140315', '20140316']\n",
+       "    _file_times:                 ['000001', '000001']\n",
+       "    _datastream:                 twpkazrcorhiC1.c1\n",
+       "    _arm_standards_flag:         1
" + ], + "text/plain": [ + "\n", + "Dimensions: (time: 89645, range: 516)\n", + "Coordinates:\n", + " * time (time) datetime64[ns] 2014-03-1...\n", + " * range (range) float32 2.007e+03 ... 1...\n", + "Data variables: (12/23)\n", + " base_time (time) datetime64[ns] 2014-03-1...\n", + " time_offset (time) datetime64[ns] 2014-03-1...\n", + " reflectivity_copol (time, range) float32 dask.array\n", + " qc_reflectivity_copol (time, range) int32 dask.array\n", + " gaseous_attenuation_correction_copol (time, range) float32 dask.array\n", + " qc_gaseous_attenuation_correction_copol (time, range) int32 dask.array\n", + " ... ...\n", + " qc_rh (time, range) int32 dask.array\n", + " bar_pres (time, range) float32 dask.array\n", + " qc_bar_pres (time, range) int32 dask.array\n", + " lat (time) float32 -2.06 ... -2.06\n", + " lon (time) float32 147.4 ... 147.4\n", + " alt (time) float32 4.0 4.0 ... 4.0 4.0\n", + "Attributes: (12/32)\n", + " command_line: idl -R -n kazrcor -s twp -f C1 -b 20140315 -...\n", + " Conventions: ARM-1.1\n", + " process_version: vap-kazrcor-1.6-0.el6\n", + " input_datastreams: twpkazrgeC1.b1 : 1.3 : 20140315.000001\\ntwpk...\n", + " dod_version: kazrcorhi-c1-1.3\n", + " site_id: twp\n", + " ... ...\n", + " doi: 10.5439/1228772\n", + " history: created by user ttoto on machine chalk at 20...\n", + " _file_dates: ['20140315', '20140316']\n", + " _file_times: ['000001', '000001']\n", + " _datastream: twpkazrcorhiC1.c1\n", + " _arm_standards_flag: 1" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['reflectivity', 'mean_doppler_velocity', 'spectral_width']" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [ + { + "ename": "KeyError", + "evalue": "'reflectivity'", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mKeyError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[0;32mIn[9], line 5\u001b[0m\n\u001b[1;32m 2\u001b[0m ts_display\u001b[38;5;241m.\u001b[39madd_subplots((\u001b[38;5;28mlen\u001b[39m(variables_to_plot),), figsize \u001b[38;5;241m=\u001b[39m (\u001b[38;5;241m9.5\u001b[39m,\u001b[38;5;241m4\u001b[39m\u001b[38;5;241m*\u001b[39m\u001b[38;5;28mlen\u001b[39m(variables_to_plot)))\n\u001b[1;32m 4\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m i,v \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28menumerate\u001b[39m(variables_to_plot):\n\u001b[0;32m----> 5\u001b[0m ts_ax \u001b[38;5;241m=\u001b[39m ts_display\u001b[38;5;241m.\u001b[39mplot(v, subplot_index\u001b[38;5;241m=\u001b[39m(i,), set_title\u001b[38;5;241m=\u001b[39m\u001b[43mds\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mvariables\u001b[49m\u001b[43m[\u001b[49m\u001b[43mv\u001b[49m\u001b[43m]\u001b[49m\u001b[38;5;241m.\u001b[39mattrs[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mlong_name\u001b[39m\u001b[38;5;124m'\u001b[39m],)\n\u001b[1;32m 6\u001b[0m ts_ax\u001b[38;5;241m.\u001b[39mgrid()\n\u001b[1;32m 8\u001b[0m plt\u001b[38;5;241m.\u001b[39mshow()\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/core/utils.py:453\u001b[0m, in \u001b[0;36mFrozen.__getitem__\u001b[0;34m(self, key)\u001b[0m\n\u001b[1;32m 452\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21m__getitem__\u001b[39m(\u001b[38;5;28mself\u001b[39m, key: K) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m V:\n\u001b[0;32m--> 453\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mmapping\u001b[49m\u001b[43m[\u001b[49m\u001b[43mkey\u001b[49m\u001b[43m]\u001b[49m\n", + "\u001b[0;31mKeyError\u001b[0m: 'reflectivity'" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "f666994b48ed49da969503777e133ba7", + "version_major": 2, + "version_minor": 0 + }, + "image/png": "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1tnZmUVE9thjj2WdnZ3ZW2+9lWWZ6+/JJmwT9uSTT2aVlZVZYWFhNn/+/Gznzp3D/+zuu+/ObrrpphHH/+d//mf2J3/yJ1lhYWH2uc99Ltu8eXOOJ2aqGs9euummm7KIGPW4++67cz84U854f1/6XcKW3zXevXTw4MHs1ltvzS677LJs9uzZWXNzc/bBBx/keGqmqvHup8cffzz74he/mF122WVZeXl59ld/9VfZ0aNHczw1U81//Md/fOw1kOvvyZWXZd5TAQAAQLp8xhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAnbCbZr165YtmxZzJo1K/Ly8uKVV175xHN27twZ1dXVUVxcHNddd1089dRTEz8oAABAooTtBHv//fdj3rx58cQTT5zT8YcPH46lS5dGXV1ddHZ2xkMPPRSrVq2KF198cYInBQAASFNelmXZZA9xqcjLy4uXX345li9fftZjvv3tb8err74aBw8eHF5ramqKn/3sZ7Fv374cTAkAAJCWgskegJH27dsX9fX1I9Zuv/322LJlS3z44Ycxffr0Mc8bGhqKoaGh4eenT5+Od999N2bMmBF5eXkTOjMAAFzqsiyL48ePx6xZs2LaNG+MzTVhO8X09vZGWVnZiLWysrI4efJk9PX1RXl5+ZjntbS0xIYNG3IxIgAAcBZHjhyJ2bNnT/YYlxxhOwX9/h3WM+8W/7g7r+vWrYvm5ubh5wMDA3HttdfGkSNHoqSkZGIGBQAAIiJicHAwKioq4g//8A8ne5RLkrCdYq6++uro7e0dsXbs2LEoKCiIGTNmnPW8oqKiKCoqGrVeUlIibAEAIEd8DHByePP3FLNo0aJob28fsbZjx46oqak56+drAQAALmXCdoK999570dXVFV1dXRHx0df5dHV1RXd3d0R89BbixsbG4eObmprirbfeiubm5jh48GBs3bo1tmzZEg888MBkjA8AADDleSvyBNu/f3/cfPPNw8/P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7dixY1FQUBAzZsw463lFRUVRVFQ0ar2kpETYAgBAjvgY4OTw5u8pZtGiRdHe3j5ibceOHVFTU3PWz9cCAABcyoTtBHvvvfeiq6srurq6IuKjr/Pp6uqK7u7uiPjoLcSNjY3Dxzc1NcVbb70Vzc3NcfDgwdi6dWts2bIlHnjggckYHwAAYMrzVuQJtn///rj55puHn5/5HOzdd98d27Zti56enuHIjYioqqqKtra2WLNmTTz55JMxa9asePzxx+NrX/tazmcHAABIge+xvUgNDg5GaWlpDAwM+IwtAABMMNffk8tbkQEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAnbHNm0aVNUVVVFcXFxVFdXx+7duz/2+O3bt8e8efPi8ssvj/Ly8rj33nujv78/R9MCAACkQ9jmQGtra6xevTrWr18fnZ2dUVdXF0uWLInu7u4xj9+zZ080NjbGypUr44033ojnn38+fvrTn8Z9992X48kBAACmPmGbA4899lisXLky7rvvvpgzZ0780z/9U1RUVMTmzZvHPP6//uu/4nOf+1ysWrUqqqqq4k//9E/jG9/4Ruzfvz/HkwMAAEx9wnaCnThxIjo6OqK+vn7Een19fezdu3fMc2pra+Po0aPR1tYWWZbFO++8Ey+88ELccccduRgZAAAgKcJ2gvX19cWpU6eirKxsxHpZWVn09vaOeU5tbW1s3749GhoaorCwMK6++ur4zGc+E9///vfP+nOGhoZicHBwxAMAAOBSIGxzJC8vb8TzLMtGrZ1x4MCBWLVqVTz88MPR0dERr732Whw+fDiamprO+uu3tLREaWnp8KOiouKCzg8AADBV5WVZlk32EBezEydOxOWXXx7PP/98/MVf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yZlq3n///Zg3b1488cQT53S86+8cy0jSggULsqamphFrN954Y7Z27doxj/+7v/u77MYbbxyx9o1vfCP7yle+MmEzkobx7qWxfPGLX8w2bNhwoUcjMee7lxoaGrK///u/zx555JFs3rx5EzghqRjvXvq3f/u3rLS0NOvv78/FeCRmvPvpH//xH7PrrrtuxNrjjz+ezZ49e8JmJD0Rkb388ssfe4zr79xyxzZBJ06ciI6Ojqivrx+xXl9fH3v37h3znH379o06/vbbb4/9+/fHhx9+OGGzMrWdz176fadPn47jx4/HlVdeOREjkojz3UvPPPNMvPnmm/HII49M9Igk4nz20quvvho1NTXx3e9+N6655pq44YYb4oEHHojf/OY3uRiZKex89lNtbW0cPXo02traIsuyeOedd+KFF16IO+64IxcjcxFx/Z1bBZM9AOPX19cXp06dirKyshHrZWVl0dvbO+Y5vb29Yx5/8uTJ6Ovri/Ly8gmbl6nrfPbS7/ve974X77//fqxYsWIiRiQR57OXfvnLX8batWtj9+7dUVDgP0d85Hz20qFDh2LPnj1RXFwcL7/8cvT19cX9998f7777rs/ZXuLOZz/V1tbG9u3bo6GhIX7729/GyZMn48/+7M/i+9//fi5G5iLi+ju33LFNWF5e3ojnWZaNWvuk48da59Iz3r10xnPPPRff+c53orW1Na666qqJGo+EnOteOnXqVNx5552xYcOGuOGGG3I1HgkZz+9Lp0+fjry8vNi+fXssWLAgli5dGo899lhs27bNXVsiYnz76cCBA7Fq1ap4+OGHo6OjI1577bU4fPhwNDU15WJULjKuv3PHH5EnaObMmZGfnz/qTxqPHTs26k+Fzrj66qvHPL6goCBmzJgxYbMytZ3PXjqjtbU1Vq5cGc8//3zceuutEzkmCRjvXjp+/Hjs378/Ojs741vf+lZEfBQnWZZFQUFB7NixI2655ZaczM7Ucj6/L5WXl8c111wTpaWlw2tz5syJLMvi6NGjcf3110/ozExd57OfWlpaYvHixfHggw9GRMSXvvSluOKKK6Kuri4effRRd9k4Z66/c8sd2wQVFhZGdXV1tLe3j1hvb2+P2traMc9ZtGjRqON37NgRNTU1MX369AmblantfPZSxEd3au+555549tlnfeaIiBj/XiopKYmf//zn0dXVNfxoamqKL3zhC9HV1RULFy7M1ehMMefz+9LixYvj7bffjvfee2947Re/+EVMmzYtZs+ePaHzMrWdz3764IMPYtq0kZfI+fn5EfF/d9vgXLj+zrFJ+kur+JR+/OMfZ9OnT8+2bNmSHThwIFu9enV2xRVXZP/7v/+bZVmWrV27NrvrrruGjz906FB2+eWXZ2vWrMkOHDiQbdmyJZs+fXr2wgsvTNZLYIoY71569tlns4KCguzJJ5/Menp6hh+//vWvJ+slMEWMdy/9Pn8rMmeMdy8dP348mz17dvaXf/mX2RtvvJHt3Lkzu/7667P77rtvsl4CU8h499MzzzyTFRQUZJs2bcrefPPNbM+ePVlNTU22YMGCyXoJTBHHjx/POjs7s87Oziwissceeyzr7OzM3nrrrSzLXH9PNmGbsCeffDKrrKzMCgsLs/nz52c7d+4c/md33313dtNNN404/j//8z+zP/mTP8kKCwuzz33uc9nmzZtzPDFT1Xj20k033ZRFxKjH3XffnfvBmXLG+/vS7xK2/K7x7qWDBw9mt956a3bZZZdls2fPzpqbm7MPPvggx1MzVY13Pz3++OPZF7/4xeyyyy7LysvLs7/6q7/Kjh49muOpmWr+4z/+42OvgVx/T668LPOeCgAAANLlM7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQtP8PDcrXAZbhxkUAAAAASUVORK5CYII=", + "text/html": [ + "\n", + "
\n", + "
\n", + " Figure\n", + "
\n", + " \n", + "
\n", + " " + ], + "text/plain": [ + "Canvas(toolbar=Toolbar(toolitems=[('Home', 'Reset original view', 'home', 'home'), ('Back', 'Back to previous …" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'reflectivity'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'reflectivity'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/KAZRCOR/.ipynb_checkpoints/kazrcormd.c1-checkpoint.ipynb b/VAPs/quicklook/KAZRCOR/.ipynb_checkpoints/kazrcormd.c1-checkpoint.ipynb new file mode 100644 index 00000000..284e0d7b --- /dev/null +++ b/VAPs/quicklook/KAZRCOR/.ipynb_checkpoints/kazrcormd.c1-checkpoint.ipynb @@ -0,0 +1,2667 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# KAZRCORMD.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/kazrcor) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'kazrcormd'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2014-02-07', 'facility': 'C1', 'site': 'nsa', 'start_date': '2011-11-11'}, {'end_date': '2014-03-15', 'facility': 'C1', 'site': 'sgp', 'start_date': '2011-05-03'}]" + ] + }, + { + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "ac6764f5", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The following locations and date ranges are available for this VAP:\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
sitefacilitystart_dateend_date
0nsaC12011-11-112014-02-07
1sgpC12011-05-032014-03-15
\n", + "
" + ], + "text/plain": [ + " site facility start_date end_date\n", + "0 nsa C1 2011-11-11 2014-02-07\n", + "1 sgp C1 2011-05-03 2014-03-15" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2014-03-14'\n", + "date_end = '2014-03-15'" + ] + }, + { + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'/data/archive/sgp/sgpkazrcormdC1.c1'" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['20140314', '20140315']" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "51feea2e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['/data/archive/sgp/sgpkazrcormdC1.c1/sgpkazrcormdC1.c1.20140314.000001.nc',\n", + " '/data/archive/sgp/sgpkazrcormdC1.c1/sgpkazrcormdC1.c1.20140315.000002.nc']" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "72 files loaded\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
<xarray.Dataset>\n",
+       "Dimensions:                                   (time: 23386, range: 652)\n",
+       "Coordinates:\n",
+       "  * time                                      (time) datetime64[ns] 2014-03-1...\n",
+       "  * range                                     (range) float32 718.1 ... 2.023...\n",
+       "Data variables: (12/39)\n",
+       "    base_time                                 datetime64[ns] 2014-03-14\n",
+       "    time_offset                               (time) datetime64[ns] 2014-03-1...\n",
+       "    reflectivity_copol                        (time, range) float32 dask.array<chunksize=(23386, 652), meta=np.ndarray>\n",
+       "    qc_reflectivity_copol                     (time, range) int32 dask.array<chunksize=(23386, 652), meta=np.ndarray>\n",
+       "    gaseous_attenuation_correction_copol      (time, range) float32 dask.array<chunksize=(23386, 652), meta=np.ndarray>\n",
+       "    qc_gaseous_attenuation_correction_copol   (time, range) int32 dask.array<chunksize=(23386, 652), meta=np.ndarray>\n",
+       "    ...                                        ...\n",
+       "    qc_rh                                     (time, range) int32 dask.array<chunksize=(23386, 652), meta=np.ndarray>\n",
+       "    bar_pres                                  (time, range) float32 dask.array<chunksize=(23386, 652), meta=np.ndarray>\n",
+       "    qc_bar_pres                               (time, range) int32 dask.array<chunksize=(23386, 652), meta=np.ndarray>\n",
+       "    lat                                       float32 ...\n",
+       "    lon                                       float32 ...\n",
+       "    alt                                       float32 ...\n",
+       "Attributes: (12/33)\n",
+       "    command_line:                idl -R -n kazrcor -s sgp -f C1 -b 20140314 -...\n",
+       "    Conventions:                 ARM-1.1\n",
+       "    process_version:             vap-kazrcor-1.6-0.el6\n",
+       "    input_datastreams:           sgpkazrgeC1.b1 : 1.3 : 20140314.000001\\nsgpk...\n",
+       "    dod_version:                 kazrcormd-c1-2.0\n",
+       "    site_id:                     sgp\n",
+       "    ...                          ...\n",
+       "    doi:                         10.5439/1228771\n",
+       "    history:                     created by user ttoto on machine chalk at 20...\n",
+       "    _file_dates:                 ['20140314']\n",
+       "    _file_times:                 ['000001']\n",
+       "    _datastream:                 sgpkazrcormdC1.c1\n",
+       "    _arm_standards_flag:         1
" + ], + "text/plain": [ + "\n", + "Dimensions: (time: 23386, range: 652)\n", + "Coordinates:\n", + " * time (time) datetime64[ns] 2014-03-1...\n", + " * range (range) float32 718.1 ... 2.023...\n", + "Data variables: (12/39)\n", + " base_time datetime64[ns] 2014-03-14\n", + " time_offset (time) datetime64[ns] 2014-03-1...\n", + " reflectivity_copol (time, range) float32 dask.array\n", + " qc_reflectivity_copol (time, range) int32 dask.array\n", + " gaseous_attenuation_correction_copol (time, range) float32 dask.array\n", + " qc_gaseous_attenuation_correction_copol (time, range) int32 dask.array\n", + " ... ...\n", + " qc_rh (time, range) int32 dask.array\n", + " bar_pres (time, range) float32 dask.array\n", + " qc_bar_pres (time, range) int32 dask.array\n", + " lat float32 ...\n", + " lon float32 ...\n", + " alt float32 ...\n", + "Attributes: (12/33)\n", + " command_line: idl -R -n kazrcor -s sgp -f C1 -b 20140314 -...\n", + " Conventions: ARM-1.1\n", + " process_version: vap-kazrcor-1.6-0.el6\n", + " input_datastreams: sgpkazrgeC1.b1 : 1.3 : 20140314.000001\\nsgpk...\n", + " dod_version: kazrcormd-c1-2.0\n", + " site_id: sgp\n", + " ... ...\n", + " doi: 10.5439/1228771\n", + " history: created by user ttoto on machine chalk at 20...\n", + " _file_dates: ['20140314']\n", + " _file_times: ['000001']\n", + " _datastream: sgpkazrcormdC1.c1\n", + " _arm_standards_flag: 1" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter [0]\n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['reflectivity', 'mean_doppler_velocity', 'spectral_width']" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [ + { + "ename": "KeyError", + "evalue": "'reflectivity'", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mKeyError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[0;32mIn[9], line 5\u001b[0m\n\u001b[1;32m 2\u001b[0m ts_display\u001b[38;5;241m.\u001b[39madd_subplots((\u001b[38;5;28mlen\u001b[39m(variables_to_plot),), figsize \u001b[38;5;241m=\u001b[39m (\u001b[38;5;241m9.5\u001b[39m,\u001b[38;5;241m4\u001b[39m\u001b[38;5;241m*\u001b[39m\u001b[38;5;28mlen\u001b[39m(variables_to_plot)))\n\u001b[1;32m 4\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m i,v \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28menumerate\u001b[39m(variables_to_plot):\n\u001b[0;32m----> 5\u001b[0m ts_ax \u001b[38;5;241m=\u001b[39m ts_display\u001b[38;5;241m.\u001b[39mplot(v, subplot_index\u001b[38;5;241m=\u001b[39m(i,), set_title\u001b[38;5;241m=\u001b[39m\u001b[43mds\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mvariables\u001b[49m\u001b[43m[\u001b[49m\u001b[43mv\u001b[49m\u001b[43m]\u001b[49m\u001b[38;5;241m.\u001b[39mattrs[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mlong_name\u001b[39m\u001b[38;5;124m'\u001b[39m],)\n\u001b[1;32m 6\u001b[0m ts_ax\u001b[38;5;241m.\u001b[39mgrid()\n\u001b[1;32m 8\u001b[0m plt\u001b[38;5;241m.\u001b[39mshow()\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/core/utils.py:453\u001b[0m, in \u001b[0;36mFrozen.__getitem__\u001b[0;34m(self, key)\u001b[0m\n\u001b[1;32m 452\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21m__getitem__\u001b[39m(\u001b[38;5;28mself\u001b[39m, key: K) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m V:\n\u001b[0;32m--> 453\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mmapping\u001b[49m\u001b[43m[\u001b[49m\u001b[43mkey\u001b[49m\u001b[43m]\u001b[49m\n", + "\u001b[0;31mKeyError\u001b[0m: 'reflectivity'" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "fe6a6740ed58487aa977d2bfc40e0bdc", + "version_major": 2, + "version_minor": 0 + }, + "image/png": "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1tnZmUVE9thjj2WdnZ3ZW2+9lWWZ6+/JJmwT9uSTT2aVlZVZYWFhNn/+/Gznzp3D/+zuu+/ObrrpphHH/+d//mf2J3/yJ1lhYWH2uc99Ltu8eXOOJ2aqGs9euummm7KIGPW4++67cz84U854f1/6XcKW3zXevXTw4MHs1ltvzS677LJs9uzZWXNzc/bBBx/keGqmqvHup8cffzz74he/mF122WVZeXl59ld/9VfZ0aNHczw1U81//Md/fOw1kOvvyZWXZd5TAQAAQLp8xhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAnbCbZr165YtmxZzJo1K/Ly8uKVV175xHN27twZ1dXVUVxcHNddd1089dRTEz8oAABAooTtBHv//fdj3rx58cQTT5zT8YcPH46lS5dGXV1ddHZ2xkMPPRSrVq2KF198cYInBQAASFNelmXZZA9xqcjLy4uXX345li9fftZjvv3tb8err74aBw8eHF5ramqKn/3sZ7Fv374cTAkAAJCWgskegJH27dsX9fX1I9Zuv/322LJlS3z44Ycxffr0Mc8bGhqKoaGh4eenT5+Od999N2bMmBF5eXkTOjMAAFzqsiyL48ePx6xZs2LaNG+MzTVhO8X09vZGWVnZiLWysrI4efJk9PX1RXl5+ZjntbS0xIYNG3IxIgAAcBZHjhyJ2bNnT/YYlxxhOwX9/h3WM+8W/7g7r+vWrYvm5ubh5wMDA3HttdfGkSNHoqSkZGIGBQAAIiJicHAwKioq4g//8A8ne5RLkrCdYq6++uro7e0dsXbs2LEoKCiIGTNmnPW8oqKiKCoqGrVeUlIibAEAIEd8DHByePP3FLNo0aJob28fsbZjx46oqak56+drAQAALmXCdoK999570dXVFV1dXRHx0df5dHV1RXd3d0R89BbixsbG4eObmprirbfeiubm5jh48GBs3bo1tmzZEg888MBkjA8AADDleSvyBNu/f3/cfPPNw8/P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7dixY1FQUBAzZsw463lFRUVRVFQ0ar2kpETYAgBAjvgY4OTw5u8pZtGiRdHe3j5ibceOHVFTU3PWz9cCAABcyoTtBHvvvfeiq6srurq6IuKjr/Pp6uqK7u7uiPjoLcSNjY3Dxzc1NcVbb70Vzc3NcfDgwdi6dWts2bIlHnjggckYHwAAYMrzVuQJtn///rj55puHn5/5HOzdd98d27Zti56enuHIjYioqqqKtra2WLNmTTz55JMxa9asePzxx+NrX/tazmcHAABIge+xvUgNDg5GaWlpDAwM+IwtAABMMNffk8tbkQEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAnbHNm0aVNUVVVFcXFxVFdXx+7duz/2+O3bt8e8efPi8ssvj/Ly8rj33nujv78/R9MCAACkQ9jmQGtra6xevTrWr18fnZ2dUVdXF0uWLInu7u4xj9+zZ080NjbGypUr44033ojnn38+fvrTn8Z9992X48kBAACmPmGbA4899lisXLky7rvvvpgzZ0780z/9U1RUVMTmzZvHPP6//uu/4nOf+1ysWrUqqqqq4k//9E/jG9/4Ruzfvz/HkwMAAEx9wnaCnThxIjo6OqK+vn7Een19fezdu3fMc2pra+Po0aPR1tYWWZbFO++8Ey+88ELccccduRgZAAAgKcJ2gvX19cWpU6eirKxsxHpZWVn09vaOeU5tbW1s3749GhoaorCwMK6++ur4zGc+E9///vfP+nOGhoZicHBwxAMAAOBSIGxzJC8vb8TzLMtGrZ1x4MCBWLVqVTz88MPR0dERr732Whw+fDiamprO+uu3tLREaWnp8KOiouKCzg8AADBV5WVZlk32EBezEydOxOWXXx7PP/98/MVf/MXw+v/7f/8vurq6YufOnaPOueuuu+K3v/1tPP/888Nre/bsibq6unj77bejvLx81DlDQ0MxNDQ0/HxwcDAqKipiYGAgSkpKLvCrAgAAftfg4GCUlpa6/p4k7thOsMLCwqiuro729vYR6+3t7VFbWzvmOR988EFMmzby/5r8/PyI+OhO71iKioqipKRkxAMAAOBSIGxzoLm5OZ5++unYunVrHDx4MNasWRPd3d3Dby1et25dNDY2Dh+/bNmyeOmll2Lz5s1x6NCheP3112PVqlWxYMGCmDVr1mS9DAAAgCmpYLIHuBQ0NDREf39/bNy4MXp6emLu3LnR1tYWlZWVERHR09Mz4jtt77nnnjh+/Hg88cQT8bd/+7fxmc98Jm655Zb4h3/4h8l6CQAAAFOWz9hepLzHHwAAcsf19+TyVmQAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCNkc2bdoUVVVVUVxcHNXV1bF79+6PPX5oaCjWr18flZWVUVRUFJ///Odj69atOZoWAAAgHQWTPcCloLW1NVavXh2bNm2KxYsXxw9+8INYsmRJHDhwIK699toxz1mxYkW88847sWXLlvijP/qjOHbsWJw8eTLHkwMAAEx9eVmWZZM9xMVu4cKFMX/+/Ni8efPw2pw5c2L58uXR0tIy6vjXXnstvv71r8ehQ4fiyiuvPK+fOTg4GKWlpTEwMBAlJSXnPTsAAPDJXH9PLm9FnmAnTpyIjo6OqK+vH7FeX18fe/fuHfOcV199NWpqauK73/1uXHPNNXHDDTfEAw88EL/5zW9yMTIAAEBSvBV5gvX19cWpU6eirKxsxHpZWVn09vaOec6hQ4diz549UVxcHC+//HL09fXF/fffH+++++5ZP2c7NDQUQ0NDw88HBwcv3IsAAACYwtyxzZG8vLwRz7MsG7V2xunTpyMvLy+2b98eCxYsiKVLl8Zjjz0W27ZtO+td25aWligtLR1+VFRUXPDXAAAAMBUJ2wk2c+bMyM/PH3V39tixY6Pu4p5RXl4e11xzTZSWlg6vzZkzJ7Isi6NHj455zrp162JgYGD4ceTIkQv3IgAAAKYwYTvBCgsLo7q6Otrb20est7e3R21t7ZjnLF68ON5+++147733htd+8YtfxLRp02L27NljnlNUVBQlJSUjHgAAAJcCYZsDzc3N8fTTT8fWrVvj4MGDsWbNmuju7o6mpqaI+Ohua2Nj4/Dxd955Z8yYMSPuvffeOHDgQOzatSsefPDB+Ou//uu47LLLJutlAAAATEn+8qgcaGhoiP7+/ti4cWP09PTE3Llzo62tLSorKyMioqenJ7q7u4eP/4M/+INob2+Pv/mbv4mampqYMWNGrFixIh599NHJegkAAABTlu+xvUj5Hi0AAMgd19+Ty1uRAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmxzZNOmTVFVVRXFxcVRXV0du3fvPqfzXn/99SgoKIgvf/nLEzsgAABAooRtDrS2tsbq1atj/fr10dnZGXV1dbFkyZLo7u7+2PMGBgaisbExvvrVr+ZoUgAAgPTkZVmWTfYQF7uFCxfG/PnzY/PmzcNrc+bMieXLl0dLS8tZz/v6178e119/feTn58crr7wSXV1d5/wzBwcHo7S0NAYGBqKkpOTTjA8AAHwC19+Tyx3bCXbixIno6OiI+vr6Eev19fWxd+/es573zDPPxJtvvhmPPPLIOf2coaGhGBwcHPEAAAC4FAjbCdbX1xenTp2KsrKyEetlZWXR29s75jm//OUvY+3atbF9+/YoKCg4p5/T0tISpaWlw4+KiopPPTsAAEAKhG2O5OXljXieZdmotYiIU6dOxZ133hkbNmyIG2644Zx//XXr1sXAwMDw48iRI596ZgAAgBSc2+1AztvMmTMjPz9/1N3ZY8eOjbqLGxFx/Pjx2L9/f3R2dsa3vvWtiIg4ffp0ZFkWBQUFsWPHjrjllltGnVdUVBRFRUUT8yIAAACmMHdsJ1hhYWFUV1dHe3v7iPX29vaora0ddXxJSUn8/Oc/j66uruFHU1NTfOELX4iurq5YuHBhrkYHAABIgju2OdDc3Bx33XVX1NTUxKJFi+KHP/xhdHd3R1NTU0R89DbiX/3qV/GjH/0opk2bFnPnzh1x/lVXXRXFxcWj1gEAABC2OdHQ0BD9/f2xcePG6Onpiblz50ZbW1tUVlZGRERPT88nfqctAAAAY/M9thcp36MFAAC54/p7cvmMLQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGGbI5s2bYqqqqooLi6O6urq2L1791mPfemll+K2226Lz372s1FSUhKLFi2Kn/zkJzmcFgAAIB3CNgdaW1tj9erVsX79+ujs7Iy6urpYsmRJdHd3j3n8rl274rbbbou2trbo6OiIm2++OZYtWxadnZ05nhwAAGDqy8uyLJvsIS52CxcujPnz58fmzZuH1+bMmRPLly+PlpaWc/o1/viP/zgaGhri4YcfPqfjBwcHo7S0NAYGBqKkpOS85gYAAM6N6+/J5Y7tBDtx4kR0dHREfX39iPX6+vrYu3fvOf0ap0+fjuPHj8eVV145ESMCAAAkrWCyB7jY9fX1xalTp6KsrGzEellZWfT29p7Tr/G9730v3n///VixYsVZjxkaGoqhoaHh54ODg+c3MAAAQGLcsc2RvLy8Ec+zLBu1NpbnnnsuvvOd70Rra2tcddVVZz2upaUlSktLhx8VFRWfemYAAIAUCNsJNnPmzMjPzx91d/bYsWOj7uL+vtbW1li5cmX8y7/8S9x6660fe+y6detiYGBg+HHkyJFPPTsAAEAKhO0EKywsjOrq6mhvbx+x3t7eHrW1tWc977nnnot77rknnn322bjjjjs+8ecUFRVFSUnJiAcAAMClwGdsc6C5uTnuuuuuqKmpiUWLFsUPf/jD6O7ujqampoj46G7rr371q/jRj34UER9FbWNjY/zzP/9zfOUrXxm+23vZZZdFaWnppL0OAACAqUjY5kBDQ0P09/fHxo0bo6enJ+bOnRttbW1RWVkZERE9PT0jvtP2Bz/4QZw8eTK++c1vxje/+c3h9bvvvju2bduW6/EBAACmNN9je5HyPVoAAJA7rr8nl8/YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdjmyKZNm6KqqiqKi4ujuro6du/e/bHH79y5M6qrq6O4uDiuu+66eOqpp3I0KQAAQFqEbQ60trbG6tWrY/369dHZ2Rl1dXWxZMmS6O7uHvP4w4cPx9KlS6Ouri46OzvjoYceilWrVsWLL76Y48kBAACmvrwsy7LJHuJit3Dhwpg/f35s3rx5eG3OnDmxfPnyaGlpGXX8t7/97Xj11Vfj4MGDw2tNTU3xs5/9LPbt23dOP3NwcDBKS0tjYGAgSkpKPv2LAAAAzsr19+QqmOwBLnYnTpyIjo6OWLt27Yj1+vr62Lt375jn7Nu3L+rr60es3X777bFly5b48MMPY/r06aPOGRoaiqGhoeHnAwMDEfHRv2AAAMDEOnPd7b7h5BC2E6yvry9OnToVZWVlI9bLysqit7d3zHN6e3vHPP7kyZPR19cX5eXlo85paWmJDRs2jFqvqKj4FNMDAADj0d/fH6WlpZM9xiVH2OZIXl7eiOdZlo1a+6Tjx1o/Y926ddHc3Dz8/Ne//nVUVlZGd3e3f7H4VAYHB6OioiKOHDnibTV8KvYSF5L9xIViL3GhDAwMxLXXXhtXXnnlZI9ySRK2E2zmzJmRn58/6u7ssWPHRt2VPePqq68e8/iCgoKYMWPGmOcUFRVFUVHRqPXS0lK/SXNBlJSU2EtcEPYSF5L9xIViL3GhTJvm7+edDP5Xn2CFhYVRXV0d7e3tI9bb29ujtrZ2zHMWLVo06vgdO3ZETU3NmJ+vBQAAuJQJ2xxobm6Op59+OrZu3RoHDx6MNWvWRHd3dzQ1NUXER28jbmxsHD6+qakp3nrrrWhubo6DBw/G1q1bY8uWLfHAAw9M1ksAAACYsrwVOQcaGhqiv78/Nm7cGD09PTF37txoa2uLysrKiIjo6ekZ8Z22VVVV0dbWFmvWrIknn3wyZs2aFY8//nh87WtfO+efWVRUFI888siYb0+G8bCXuFDsJS4k+4kLxV7iQrGXJpfvsQUAACBp3ooMAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhm7BNmzZFVVVVFBcXR3V1dezevftjj9+5c2dUV1dHcXFxXHfddfHUU0/laFKmuvHspZdeeiluu+22+OxnPxslJSWxaNGi+MlPfpLDaZnKxvv70hmvv/56FBQUxJe//OWJHZBkjHcvDQ0Nxfr166OysjKKiori85//fGzdujVH0zLVjXc/bd++PebNmxeXX355lJeXx7333hv9/f05mpapateuXbFs2bKYNWtW5OXlxSuvvPKJ57j+zh1hm6jW1tZYvXp1rF+/Pjo7O6Ouri6WLFky4muDftfhw4dj6dKlUVdXF52dnfHQQw/FqlWr4sUXX8zx5Ew1491Lu3btittuuy3a2tqio6Mjbr755li2bFl0dnbmeHKmmvHupTMGBgaisbExvvrVr+ZoUqa689lLK1asiH//93+PLVu2xP/8z//Ec889FzfeeGMOp2aqGu9+2rNnTzQ2NsbKlSvjjTfeiOeffz5++tOfxn333ZfjyZlq3n///Zg3b1488cQT53S86+8cy0jSggULsqamphFrN954Y7Z27doxj/+7v/u77MYbbxyx9o1vfCP7yle+MmEzkobx7qWxfPGLX8w2bNhwoUcjMee7lxoaGrK///u/zx555JFs3rx5EzghqRjvXvq3f/u3rLS0NOvv78/FeCRmvPvpH//xH7PrrrtuxNrjjz+ezZ49e8JmJD0Rkb388ssfe4zr79xyxzZBJ06ciI6Ojqivrx+xXl9fH3v37h3znH379o06/vbbb4/9+/fHhx9+OGGzMrWdz176fadPn47jx4/HlVdeOREjkojz3UvPPPNMvPnmm/HII49M9Igk4nz20quvvho1NTXx3e9+N6655pq44YYb4oEHHojf/OY3uRiZKex89lNtbW0cPXo02traIsuyeOedd+KFF16IO+64IxcjcxFx/Z1bBZM9AOPX19cXp06dirKyshHrZWVl0dvbO+Y5vb29Yx5/8uTJ6Ovri/Ly8gmbl6nrfPbS7/ve974X77//fqxYsWIiRiQR57OXfvnLX8batWtj9+7dUVDgP0d85Hz20qFDh2LPnj1RXFwcL7/8cvT19cX9998f7777rs/ZXuLOZz/V1tbG9u3bo6GhIX7729/GyZMn48/+7M/i+9//fi5G5iLi+ju33LFNWF5e3ojnWZaNWvuk48da59Iz3r10xnPPPRff+c53orW1Na666qqJGo+EnOteOnXqVNx5552xYcOGuOGGG3I1HgkZz+9Lp0+fjry8vNi+fXssWLAgli5dGo899lhs27bNXVsiYnz76cCBA7Fq1ap4+OGHo6OjI1577bU4fPhwNDU15WJULjKuv3PHH5EnaObMmZGfnz/qTxqPHTs26k+Fzrj66qvHPL6goCBmzJgxYbMytZ3PXjqjtbU1Vq5cGc8//3zceuutEzkmCRjvXjp+/Hjs378/Ojs741vf+lZEfBQnWZZFQUFB7NixI2655ZaczM7Ucj6/L5WXl8c111wTpaWlw2tz5syJLMvi6NGjcf3110/ozExd57OfWlpaYvHixfHggw9GRMSXvvSluOKKK6Kuri4effRRd9k4Z66/c8sd2wQVFhZGdXV1tLe3j1hvb2+P2traMc9ZtGjRqON37NgRNTU1MX369AmblantfPZSxEd3au+555549tlnfeaIiBj/XiopKYmf//zn0dXVNfxoamqKL3zhC9HV1RULFy7M1ehMMefz+9LixYvj7bffjvfee2947Re/+EVMmzYtZs+ePaHzMrWdz3764IMPYtq0kZfI+fn5EfF/d9vgXLj+zrFJ+kur+JR+/OMfZ9OnT8+2bNmSHThwIFu9enV2xRVXZP/7v/+bZVmWrV27NrvrrruGjz906FB2+eWXZ2vWrMkOHDiQbdmyJZs+fXr2wgsvTNZLYIoY71569tlns4KCguzJJ5/Menp6hh+//vWvJ+slMEWMdy/9Pn8rMmeMdy8dP348mz17dvaXf/mX2RtvvJHt3Lkzu/7667P77rtvsl4CU8h499MzzzyTFRQUZJs2bcrefPPNbM+ePVlNTU22YMGCyXoJTBHHjx/POjs7s87Oziwissceeyzr7OzM3nrrrSzLXH9PNmGbsCeffDKrrKzMCgsLs/nz52c7d+4c/md33313dtNNN404/j//8z+zP/mTP8kKCwuzz33uc9nmzZtzPDFT1Xj20k033ZRFxKjH3XffnfvBmXLG+/vS7xK2/K7x7qWDBw9mt956a3bZZZdls2fPzpqbm7MPPvggx1MzVY13Pz3++OPZF7/4xeyyyy7LysvLs7/6q7/Kjh49muOpmWr+4z/+42OvgVx/T668LPOeCgAAANLlM7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQtP8PDcrXAZbhxkUAAAAASUVORK5CYII=", + "text/html": [ + "\n", + "
\n", + "
\n", + " Figure\n", + "
\n", + " \n", + "
\n", + " " + ], + "text/plain": [ + "Canvas(toolbar=Toolbar(toolitems=[('Home', 'Reset original view', 'home', 'home'), ('Back', 'Back to previous …" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'reflectivity'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'reflectivity'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/KAZRCOR/KAZRCOR_tutorial.ipynb b/VAPs/quicklook/KAZRCOR/KAZRCOR_tutorial.ipynb new file mode 100644 index 00000000..6ad0056b --- /dev/null +++ b/VAPs/quicklook/KAZRCOR/KAZRCOR_tutorial.ipynb @@ -0,0 +1,838 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# KAZRCORGE.C1 Notebook\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/kazrcor) for more information about this vap." + ] + }, + { + "cell_type": "markdown", + "id": "97097763", + "metadata": {}, + "source": [ + "In this notebook, we demonstrate the workflow to explore ARM vap data (using kazrcorge as an example.) Value-added products (VAPs) are higher-order data products that have been analyzed and processed to ease scientists’ use of ARM data in atmospheric research and global climate models. \n", + "Here is the main content we will cover." + ] + }, + { + "cell_type": "markdown", + "id": "eddec40f", + "metadata": {}, + "source": [ + "# Table of Content\n", + "## Access the data\n", + "* How to retrieve the data\n", + "* Data path and file name conventions\n", + "* Load data\n", + "## Explore the data\n", + "* NetCDF Data structure\n", + "* Xarray essentials\n", + "* Xarray Variable\n", + "## Plot the data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "import random\n", + "\n", + "import glob\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "2372d5be", + "metadata": {}, + "source": [ + "## Access the data" + ] + }, + { + "cell_type": "markdown", + "id": "5b0d9684", + "metadata": {}, + "source": [ + "### How to retrieve the data\n", + "We assume the path \"/data/archive\" is available where you are running this notebook. You can use `os.path.exists(\"/data/archive\")` to verify if the path exists at your machine.\n", + "\n", + "\n", + "### Data path and file name conventions\n", + "There several common terminologies regarding ARM data, for example, data-stream-name, data-level, site, facility, instrument, etc. (For more details, please see [ARM Data File Standards Version 1.2](https://www.google.com/search?q=arm+datastream+facility+cite+definition&rlz=1C1GCEJ_enUS1029US1029&ei=hb41ZICrBPukqtsPvMODIA&ved=0ahUKEwjAgeiS0aL-AhV7kmoFHbzhAAQQ4dUDCBA&uact=5&oq=arm+datastream+facility+cite+definition&gs_lcp=Cgxnd3Mtd2l6LXNlcnAQAzIFCAAQogQyBQgAEKIEMgUIABCiBDIFCAAQogQyBQgAEKIEOgoIABBHENYEELADOgoIIRCgARDDBBAKSgQIQRgAUJ4DWIoOYO0PaAFwAXgAgAGMAYgBuQmSAQMyLjmYAQCgAQHIAQjAAQE&sclient=gws-wiz-serp).) \n", + "\n", + "For example, this notebook is called `kazrcorge.c1`, where `kazrcorge` is the \"datastream name\", and the `{process.ds_class_level}}` is the \"data level\".\n", + "\n", + "This datastream also contains site `nsa` and facility `C1`. (Note: individual datastream might have multiple site-facility pairs.)\n", + "In such a case, the data of this data-stream is stored at `/data/archive/nsa/nsakazrcorgeC1.c1`, which is in the format of `//.`. We can use the following method to assign the data-stream directory `datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )`\n", + "\n", + "The data files under datastream_dir also follows naming conventions. But once reach the datastream_dir level, the most import file naming convention to differentiate the files is \"yyyyMMdd.hhmmss\", which comes handy to filter out files based on datetime. For example, we can use `glob.glob(f'{datastream_dir}/*.200709*.*')` to filter files in 2007 September.\n", + "\n", + "(Note: refer to the https://adc.arm.gov/discovery/#/ and https://adc.arm.gov/solr8/metadata/select API to explore ARM datastream and assoicated available sites and facilities\n", + "\n", + "Please see the following examples in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d7e9eb85", + "metadata": {}, + "outputs": [], + "source": [ + "# Verify if DATA_DIR path exists\n", + "DATA_DIR = \"/data/archive\"\n", + "os.path.exists(DATA_DIR)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "586993fd", + "metadata": {}, + "outputs": [], + "source": [ + "# Speicify datastream_dir following the path conventions and check its existence\n", + "DATASTREAM_NAME = \"kazrcorge\"\n", + "DATA_LEVEL = \"c1\"\n", + "site = \"nsa\"\n", + "facility = \"C1\"\n", + "datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )\n", + "print(datastream_dir)\n", + "print(os.path.exists(datastream_dir))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0742f7c1", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: list 5 (random) files under datastream_dir\n", + "files = os.listdir(datastream_dir)\n", + "files[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39b98a36", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: get most recent file\n", + "list_of_files = glob.glob(f\"{datastream_dir}/*\") # * means all if need specific format then *.csv\n", + "latest_file = max(list_of_files, key=os.path.getctime)\n", + "latest_file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "902d514e", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus sort datastream files based on datetime\n", + "files = os.listdir(datastream_dir)\n", + "file_sorted = files.copy()\n", + "file_sorted.sort() \n", + "print(file_sorted[:5])\n", + "\n", + "# to reverse\n", + "file_sorted_reverse = files.copy() \n", + "file_sorted_reverse.sort(reverse=True)\n", + "print(file_sorted_reverse[:5])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec5923b2", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: pattern matching\n", + "# filter the 200709** files under datastream_dir\n", + "files_filter = glob.glob(f'{datastream_dir}/*.200709*.*')\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c4a4aa18", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: use armnotebook_utils.file_filter (TODO) to filter files based on datastream info \n" + ] + }, + { + "cell_type": "markdown", + "id": "cfeb9efc", + "metadata": {}, + "source": [ + "### Load data\n", + "The arm data is stored in [NetCDF](https://en.wikipedia.org/wiki/NetCDF#:~:text=%22NetCDF%20(network%20Common%20Data%20Form,format%20for%20representing%20scientific%20data.) format. We can use xarray's `open_dataset` method to load single file, or `open_mfdataset` to open multiple files. (Note: the latter will still return a single xarray dataset object by combining multiple files.)\n", + "\n", + "Note: open_dataset keeps the file handle open and lazy loads its contents. All parameters are passed directly to open_dataset. It is a preferable method over load_dataset for memory-effiency. (more details, please see [xarray.open_dataset](https://docs.xarray.dev/en/stable/generated/xarray.open_dataset.html))\n", + "\n", + "See the following example in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a440a329", + "metadata": {}, + "outputs": [], + "source": [ + "# open single file\n", + "full_path = latest_file\n", + "print(full_path)\n", + "ds_single = xr.open_dataset(full_path)\n", + "ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0143a3d", + "metadata": {}, + "outputs": [], + "source": [ + "print(ds_single)\n", + "print(type(ds_single))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c0f8939", + "metadata": {}, + "outputs": [], + "source": [ + "# open multiple files\n", + "n_files = 3\n", + "full_paths = [os.path.join(datastream_dir, f_path) for f_path in file_sorted_reverse[:n_files]]\n", + "print(full_paths)\n", + "try: # Note: sometimes multiple files cannot be merged, so we used try except here.\n", + " ds_mutiple = xr.open_mfdataset(full_paths)\n", + " # ds_mutiple\n", + " print(type(ds_mutiple))\n", + " print(ds_mutiple)\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "3fa59943", + "metadata": {}, + "source": [ + "## Explore the data" + ] + }, + { + "cell_type": "markdown", + "id": "571f69b9", + "metadata": {}, + "source": [ + "### NetCDF Data structure \n", + "\n", + "\n", + "(If you are confident with NetCDF basics and xarray essentials, feel free to skip this session.)\n", + "Before we dive into data exploration, there are some eseential concpets we should be familiar with (shown below): \n", + "* Dataset\n", + "* Data array\n", + "* Variable\n", + "* Dimenssion\n", + "* Coordinate\n", + "* Data Type\n", + "* Meta Data (Attributes)\n", + "\n", + "We will not go into details about NetCDF basics and here are some references you might find helpful\n", + "* [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#)\n", + "* [Network Common Data Form (NetCDF)](https://www.unidata.ucar.edu/software/netcdf/) and [A Brief History of (netCDF) Time](https://www.unidata.ucar.edu/software/netcdf/time/recs.html)\n", + "\n", + "\n", + "\n", + "### Xarray essentials\n", + "Earlier we introduced the xarray pacakge and used `open_dataset` and `open_mfdataset` to retrieve NetCDF data file as an xarray Dataset object. Recall that you can review the data in a notebook by using `print(ds)` or `ds`. \n", + "\n", + "Using xarray to retrieve the aforementioned NetCDF basics is straightforwared. In general,\n", + "* Dataset: `ds`\n", + "* Data array: `ds.variables`\n", + "* Variable: `ds.ds.variables`\n", + "* Dimenssion: `ds.dims`\n", + "* Coordinate: `ds.coords`\n", + "* Data Type: `type`\n", + "* Meta Data (Attributes)\n", + "\n", + "Also, here are some references if you are new to xarray\n", + "* [Xarray in 45 minutes](https://tutorial.xarray.dev/overview/xarray-in-45-min.html)\n", + "* [Handling NetCDF Files using XArray for Absolute Beginners](https://towardsdatascience.com/handling-netcdf-files-using-xarray-for-absolute-beginners-111a8ab4463f)\n", + "\n", + "Try the following commands in action\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b3eac323", + "metadata": {}, + "outputs": [], + "source": [ + "ds = ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77ecf85d", + "metadata": {}, + "outputs": [], + "source": [ + "# Dataset \n", + "ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25e7de09", + "metadata": {}, + "outputs": [], + "source": [ + "# Data array, Variable\n", + "# Note: the info can be overwhelming. Do not attempt to grasp everything at the first glance.\n", + "ds.variables" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c41b67e", + "metadata": {}, + "outputs": [], + "source": [ + "# dimenssions\n", + "ds.dims" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "156f1dfc", + "metadata": {}, + "outputs": [], + "source": [ + "# coordinates\n", + "ds.coords" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "277d6064", + "metadata": {}, + "outputs": [], + "source": [ + "# Meta Data (Attributes)\n", + "ds.attrs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d334681f", + "metadata": {}, + "outputs": [], + "source": [ + "# type\n", + "print(type(ds))\n", + "print(type(ds.variables))\n", + "print(type(ds.dims))\n", + "print(type(ds.coords))\n", + "print(type(ds.attrs))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "643399d6", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: retrieve variable names only\n", + "list(ds.variables)" + ] + }, + { + "cell_type": "markdown", + "id": "1cf36878", + "metadata": {}, + "source": [ + "#### Discussion: variable vs. coordinates vs. dimenssions. \n", + "From [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#):\n", + "\n", + "* Dimession: A dimension may be used to represent a real physical dimension, for example, time, latitude, longitude, or height. A dimension might also be used to index other quantities, for example station or model-run-number.\n", + "A netCDF dimension has both a name and a length. A dimension length is an arbitrary positive integer, except that one dimension in a netCDF dataset can have the length UNLIMITED.\n", + "\n", + "* Variables: Variables are used to store the bulk of the data in a netCDF dataset. A variable represents an array of values of the same type. A scalar value is treated as a 0-dimensional array. A variable has a name, a data type, and a shape described by its list of dimensions specified when the variable is created. A variable may also have associated attributes, which may be added, deleted or changed after the variable is created.\n", + "\n", + "* Coordinate (Variables): It is legal for a variable to have the same name as a dimension. Such variables have no special meaning to the netCDF library. However there is a convention that such variables should be treated in a special way by software using this library.\n", + "A variable with the same name as a dimension is called a coordinate variable. It typically defines a physical coordinate corresponding to that dimension.\n", + "\n", + "\n", + "Tips: By definition, coordinates and dimenssions are also variable. For example ds.time is a coordinate but it is also a special variable. But in practice, when we talk about variable, it implies regular/non-coordinate variable. We will use this convention for the remaining notebook. " + ] + }, + { + "cell_type": "markdown", + "id": "d47120f9", + "metadata": {}, + "source": [ + "### Xarray Variable\n", + "\n", + "\n", + "We can use `ds.variables` to access varialbes of a dataset. But many times viewing print-out the whole datasets can be overwhemling. Instead, we would work on the individual variable (also called Xarray data array.) using the `ds[var_name]` syntax, where `var_name` is the name of the variable. See the following examples in action.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5a57e136", + "metadata": {}, + "outputs": [], + "source": [ + "print(type(ds[\"time\"]))\n", + "ds[\"time\"]" + ] + }, + { + "cell_type": "markdown", + "id": "51ec643e", + "metadata": {}, + "source": [ + "#### Variable properties\n", + "For individual varible, the following properties are mostly used with the assocaited xarray method to retrieve them. (assuming var = ds[var_name])\n", + "* name: `var.name`\n", + "* data content: `var.data`\n", + "* attributes: `var.attrs`\n", + "* dimenstions: `var.dims`\n", + "* data type: `var.data.dtype`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a632a525", + "metadata": {}, + "outputs": [], + "source": [ + "var_name = \"time\"\n", + "var = ds[var_name]\n", + "\n", + "print(\"var.name: \\n\", var.name, \"\\n\")\n", + "print(\"var.data: \\n\", var.data, \"\\n\")\n", + "print(\"var.attrs: \\n\", var.attrs, \"\\n\")\n", + "print(\"var.dims: \\n\", var.dims, \"\\n\")\n", + "print(\"var.data.dtype: \\n\", var.data.dtype, \"\\n\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b256b07f", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: indivdual variable info table (collect and display variable info as a tabular format)\n", + "df_info = pd.DataFrame()\n", + "df_info[\"var_name\"] = list(ds.variables)\n", + "df_info[\"dims\"] = df_info.var_name.apply(lambda x: ds[x].dims)\n", + "df_info[\"is_dim\"] = df_info.var_name.apply(lambda x: x in ds.dims)\n", + "df_info[\"n_dim\"] = df_info.var_name.apply(lambda x: len(ds[x].dims))\n", + "df_info[\"attrs\"] = df_info.var_name.apply(lambda x: ds[x].attrs)\n", + "df_info[\"dtype\"] = df_info.var_name.apply(lambda x: ds[x].data.dtype)\n", + "df_info" + ] + }, + { + "cell_type": "markdown", + "id": "e6a36b96", + "metadata": {}, + "source": [ + "### Data cleaning/Preprocessing (skipped)\n", + "Data cleaning and preprocessing is an important stage in the the data analysis pipeline. However it will be out of the scope of this notebook. For more information about data cleaning and preprocessing basics, here are some references. \n", + "* [Xarray Fundamentals](https://earth-env-data-science.github.io/lectures/xarray/xarray.html)\n", + "* [Xarray Tutorial — Pangeo Gallery documentation](http://gallery.pangeo.io/repos/pangeo-data/pangeo-tutorial-gallery/xarray.html)\n", + "* [Pythonic Data Cleaning With pandas and NumPy](https://realpython.com/python-data-cleaning-numpy-pandas/)\n", + "* [Pandas - Cleaning Data](https://www.w3schools.com/python/pandas/pandas_cleaning.asp)" + ] + }, + { + "cell_type": "markdown", + "id": "ae6e1a69", + "metadata": {}, + "source": [ + "### Plotting\n", + "Data visualization (or plotting) is another important data analysis topic and deserves its own discussion. Here in this notebook we will only demonstrate basic tool for simple data visualization tasks.\n", + "\n", + "Here are some reference you might find useful:\n", + "* xarray plotting: https://docs.xarray.dev/en/stable/user-guide/plotting.html\n", + "* Atmospheric Community Toolkit (ACT): https://arm-doe.github.io/ACT/index.html\n", + "\n", + "Note: this notebook is auto-generated using a template. It uses a general idea to select variable(s) to plot and is not customized to each indivdual notebook. Feel free to change the variables in intrrests, especially certain figure is failed to plot." + ] + }, + { + "cell_type": "markdown", + "id": "7e417fb2", + "metadata": {}, + "source": [ + "#### 1-dimenssional basic time series plot\n", + "\n", + "For the following plot we would like to find variables such that\n", + "* it has one and only one dimession\n", + "* \"time\" is its coordinate variable\n", + "* it is not a dimenssion itself,\n", + "* it is not a special variable with substrings within [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "\n", + "Please see the following example in action. (For more details about pandas filtering, please see the following references.)\n", + "* [pandas: multiple conditions while indexing data frame](https://stackoverflow.com/questions/22591174/pandas-multiple-conditions-while-indexing-data-frame-unexpected-behavior)\n", + "* [pandas.DataFrame.apply](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.apply.html)\n", + "* [How to test if a string contains one of the substrings in a list...](https://stackoverflow.com/questions/26577516/how-to-test-if-a-string-contains-one-of-the-substrings-in-a-list-in-pandas" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30edac2d", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==1, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter = df_info[(df_info.n_dim == 1) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4777a659", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_1d = df_filter.var_name.values[0]\n", + " var_1d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb5ae985", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " ds[var_1d].plot()\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "9e41066e", + "metadata": {}, + "source": [ + "#### 2-dimenssional basic plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09bd4adc", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==2, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_2 = df_info[(df_info.n_dim == 2) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2cdea1a9", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_2d = df_filter_2.var_name.values[0]\n", + " var_2d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5f48879", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " print(ds[var_2d].dims)\n", + " # ds[var_2d].plot()\n", + "\n", + " # conventionally, use \"time\" as x-axis\n", + " ds[var_2d].plot(x=\"time\")\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "7554b2a6", + "metadata": {}, + "source": [ + "#### qc-plotting (optional)\n", + "\n", + "Note: act qc plotting has more strict requirement, one of them is the associated qc variable need to have \"flag_masks\" attributes. Which is added by using ds = act.io.armfiles.read_netcdf(files_list), then ds.clean.cleanup()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71c2096f", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " ds_act = act.io.armfiles.read_netcdf(full_path)\n", + " print(type(ds_act))\n", + " ds_act.clean.cleanup()\n", + "\n", + " # or \n", + " # ds.clean.cleanup()\n", + "except Exception as e:\n", + " print(\"ERROR\", e)\n", + " ds_act = ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "844f8505", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter valid variables for ACT qc plotting\n", + "condition = (\"qc_\" + df_info.var_name).apply(lambda x: ds_act[x].attrs.get(\"flag_masks\") is not None \n", + " if x in list(ds.variables) else False)\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_3 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings))) &\n", + " condition\n", + " ]\n", + "df_filter_3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd38cc6", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "try:\n", + " qc_variable = df_filter_3.var_name.values[0]\n", + " print(qc_variable)\n", + "except Exception as e:\n", + " print(e)\n", + " \n", + "try:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + " qc_ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "d76e4d27", + "metadata": {}, + "source": [ + "#### bonus: choose variables to plot from a dropdown menu " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb733804", + "metadata": {}, + "outputs": [], + "source": [ + "# Valid variables filtering\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_4 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82d5c20d", + "metadata": {}, + "outputs": [], + "source": [ + "# example 1: using xarray plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2fc9aaa4", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# fig, ax = plt.subplots(figsize=(10, 4))\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "# fig.clear() # Remove old lines from plot and plot new one\n", + "# if len(ds[var].dims)==2:\n", + "# ds[var].plot(x=\"time\", add_colorbar=False)\n", + "# else:\n", + "# ds[var].plot()\n", + "# plt.grid()\n", + "# plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fabdd802", + "metadata": {}, + "outputs": [], + "source": [ + "# example 2: using act plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "962b4186", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "\n", + "\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "\n", + "# i_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + "# i_display.add_subplots((1,), figsize=(10, 4))\n", + "# ax = i_display.plot(var, subplot_index=(0,), set_title=f\"{var} ({ds_act[var].attrs['long_name']})\",)\n", + "\n", + "# ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "# ax.grid()\n", + "# plt.show()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/KAZRCOR/kazrcorge.c1.ipynb b/VAPs/quicklook/KAZRCOR/kazrcorge.c1.ipynb new file mode 100644 index 00000000..c984815f --- /dev/null +++ b/VAPs/quicklook/KAZRCOR/kazrcorge.c1.ipynb @@ -0,0 +1,339 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# KAZRCORGE.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/kazrcor) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'kazrcorge'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2014-02-07', 'facility': 'C1', 'site': 'nsa', 'start_date': '2011-11-11'}, {'end_date': '2014-03-15', 'facility': 'C1', 'site': 'sgp', 'start_date': '2011-01-18'}, {'end_date': '2014-03-16', 'facility': 'C1', 'site': 'twp', 'start_date': '2011-03-12'}, {'end_date': '2014-05-03', 'facility': 'C3', 'site': 'twp', 'start_date': '2011-01-27'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2014-03-14'\n", + "date_end = '2014-03-15'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['reflectivity', 'mean_doppler_velocity', 'spectral_width']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'reflectivity'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'reflectivity'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/KAZRCOR/kazrcorhi.c1.ipynb b/VAPs/quicklook/KAZRCOR/kazrcorhi.c1.ipynb new file mode 100644 index 00000000..8ba736ed --- /dev/null +++ b/VAPs/quicklook/KAZRCOR/kazrcorhi.c1.ipynb @@ -0,0 +1,339 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# KAZRCORHI.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/kazrcor) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'kazrcorhi'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2014-03-16', 'facility': 'C1', 'site': 'twp', 'start_date': '2011-03-12'}, {'end_date': '2014-05-03', 'facility': 'C3', 'site': 'twp', 'start_date': '2011-01-27'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'twp', 'C1' )\n", + "\n", + "date_start = '2014-03-15'\n", + "date_end = '2014-03-16'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['reflectivity', 'mean_doppler_velocity', 'spectral_width']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'reflectivity'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'reflectivity'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/KAZRCOR/kazrcormd.c1.ipynb b/VAPs/quicklook/KAZRCOR/kazrcormd.c1.ipynb new file mode 100644 index 00000000..aaa33290 --- /dev/null +++ b/VAPs/quicklook/KAZRCOR/kazrcormd.c1.ipynb @@ -0,0 +1,339 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# KAZRCORMD.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/kazrcor) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'kazrcormd'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2014-02-07', 'facility': 'C1', 'site': 'nsa', 'start_date': '2011-11-11'}, {'end_date': '2014-03-15', 'facility': 'C1', 'site': 'sgp', 'start_date': '2011-05-03'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2014-03-14'\n", + "date_end = '2014-03-15'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['reflectivity', 'mean_doppler_velocity', 'spectral_width']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'reflectivity'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'reflectivity'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/LCLHEIGHT/LCLHEIGHT_tutorial.ipynb b/VAPs/quicklook/LCLHEIGHT/LCLHEIGHT_tutorial.ipynb new file mode 100644 index 00000000..f2282e3b --- /dev/null +++ b/VAPs/quicklook/LCLHEIGHT/LCLHEIGHT_tutorial.ipynb @@ -0,0 +1,838 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# LCL.C1 Notebook\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/lclheight) for more information about this vap." + ] + }, + { + "cell_type": "markdown", + "id": "97097763", + "metadata": {}, + "source": [ + "In this notebook, we demonstrate the workflow to explore ARM vap data (using lcl as an example.) Value-added products (VAPs) are higher-order data products that have been analyzed and processed to ease scientists’ use of ARM data in atmospheric research and global climate models. \n", + "Here is the main content we will cover." + ] + }, + { + "cell_type": "markdown", + "id": "eddec40f", + "metadata": {}, + "source": [ + "# Table of Content\n", + "## Access the data\n", + "* How to retrieve the data\n", + "* Data path and file name conventions\n", + "* Load data\n", + "## Explore the data\n", + "* NetCDF Data structure\n", + "* Xarray essentials\n", + "* Xarray Variable\n", + "## Plot the data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "import random\n", + "\n", + "import glob\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "2372d5be", + "metadata": {}, + "source": [ + "## Access the data" + ] + }, + { + "cell_type": "markdown", + "id": "5b0d9684", + "metadata": {}, + "source": [ + "### How to retrieve the data\n", + "We assume the path \"/data/archive\" is available where you are running this notebook. You can use `os.path.exists(\"/data/archive\")` to verify if the path exists at your machine.\n", + "\n", + "\n", + "### Data path and file name conventions\n", + "There several common terminologies regarding ARM data, for example, data-stream-name, data-level, site, facility, instrument, etc. (For more details, please see [ARM Data File Standards Version 1.2](https://www.google.com/search?q=arm+datastream+facility+cite+definition&rlz=1C1GCEJ_enUS1029US1029&ei=hb41ZICrBPukqtsPvMODIA&ved=0ahUKEwjAgeiS0aL-AhV7kmoFHbzhAAQQ4dUDCBA&uact=5&oq=arm+datastream+facility+cite+definition&gs_lcp=Cgxnd3Mtd2l6LXNlcnAQAzIFCAAQogQyBQgAEKIEMgUIABCiBDIFCAAQogQyBQgAEKIEOgoIABBHENYEELADOgoIIRCgARDDBBAKSgQIQRgAUJ4DWIoOYO0PaAFwAXgAgAGMAYgBuQmSAQMyLjmYAQCgAQHIAQjAAQE&sclient=gws-wiz-serp).) \n", + "\n", + "For example, this notebook is called `lcl.c1`, where `lcl` is the \"datastream name\", and the `{process.ds_class_level}}` is the \"data level\".\n", + "\n", + "This datastream also contains site `sgp` and facility `C1`. (Note: individual datastream might have multiple site-facility pairs.)\n", + "In such a case, the data of this data-stream is stored at `/data/archive/sgp/sgplclC1.c1`, which is in the format of `//.`. We can use the following method to assign the data-stream directory `datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )`\n", + "\n", + "The data files under datastream_dir also follows naming conventions. But once reach the datastream_dir level, the most import file naming convention to differentiate the files is \"yyyyMMdd.hhmmss\", which comes handy to filter out files based on datetime. For example, we can use `glob.glob(f'{datastream_dir}/*.200709*.*')` to filter files in 2007 September.\n", + "\n", + "(Note: refer to the https://adc.arm.gov/discovery/#/ and https://adc.arm.gov/solr8/metadata/select API to explore ARM datastream and assoicated available sites and facilities\n", + "\n", + "Please see the following examples in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d7e9eb85", + "metadata": {}, + "outputs": [], + "source": [ + "# Verify if DATA_DIR path exists\n", + "DATA_DIR = \"/data/archive\"\n", + "os.path.exists(DATA_DIR)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "586993fd", + "metadata": {}, + "outputs": [], + "source": [ + "# Speicify datastream_dir following the path conventions and check its existence\n", + "DATASTREAM_NAME = \"lcl\"\n", + "DATA_LEVEL = \"c1\"\n", + "site = \"sgp\"\n", + "facility = \"C1\"\n", + "datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )\n", + "print(datastream_dir)\n", + "print(os.path.exists(datastream_dir))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0742f7c1", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: list 5 (random) files under datastream_dir\n", + "files = os.listdir(datastream_dir)\n", + "files[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39b98a36", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: get most recent file\n", + "list_of_files = glob.glob(f\"{datastream_dir}/*\") # * means all if need specific format then *.csv\n", + "latest_file = max(list_of_files, key=os.path.getctime)\n", + "latest_file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "902d514e", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus sort datastream files based on datetime\n", + "files = os.listdir(datastream_dir)\n", + "file_sorted = files.copy()\n", + "file_sorted.sort() \n", + "print(file_sorted[:5])\n", + "\n", + "# to reverse\n", + "file_sorted_reverse = files.copy() \n", + "file_sorted_reverse.sort(reverse=True)\n", + "print(file_sorted_reverse[:5])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec5923b2", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: pattern matching\n", + "# filter the 200709** files under datastream_dir\n", + "files_filter = glob.glob(f'{datastream_dir}/*.200709*.*')\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c4a4aa18", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: use armnotebook_utils.file_filter (TODO) to filter files based on datastream info \n" + ] + }, + { + "cell_type": "markdown", + "id": "cfeb9efc", + "metadata": {}, + "source": [ + "### Load data\n", + "The arm data is stored in [NetCDF](https://en.wikipedia.org/wiki/NetCDF#:~:text=%22NetCDF%20(network%20Common%20Data%20Form,format%20for%20representing%20scientific%20data.) format. We can use xarray's `open_dataset` method to load single file, or `open_mfdataset` to open multiple files. (Note: the latter will still return a single xarray dataset object by combining multiple files.)\n", + "\n", + "Note: open_dataset keeps the file handle open and lazy loads its contents. All parameters are passed directly to open_dataset. It is a preferable method over load_dataset for memory-effiency. (more details, please see [xarray.open_dataset](https://docs.xarray.dev/en/stable/generated/xarray.open_dataset.html))\n", + "\n", + "See the following example in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a440a329", + "metadata": {}, + "outputs": [], + "source": [ + "# open single file\n", + "full_path = latest_file\n", + "print(full_path)\n", + "ds_single = xr.open_dataset(full_path)\n", + "ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0143a3d", + "metadata": {}, + "outputs": [], + "source": [ + "print(ds_single)\n", + "print(type(ds_single))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c0f8939", + "metadata": {}, + "outputs": [], + "source": [ + "# open multiple files\n", + "n_files = 3\n", + "full_paths = [os.path.join(datastream_dir, f_path) for f_path in file_sorted_reverse[:n_files]]\n", + "print(full_paths)\n", + "try: # Note: sometimes multiple files cannot be merged, so we used try except here.\n", + " ds_mutiple = xr.open_mfdataset(full_paths)\n", + " # ds_mutiple\n", + " print(type(ds_mutiple))\n", + " print(ds_mutiple)\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "3fa59943", + "metadata": {}, + "source": [ + "## Explore the data" + ] + }, + { + "cell_type": "markdown", + "id": "571f69b9", + "metadata": {}, + "source": [ + "### NetCDF Data structure \n", + "\n", + "\n", + "(If you are confident with NetCDF basics and xarray essentials, feel free to skip this session.)\n", + "Before we dive into data exploration, there are some eseential concpets we should be familiar with (shown below): \n", + "* Dataset\n", + "* Data array\n", + "* Variable\n", + "* Dimenssion\n", + "* Coordinate\n", + "* Data Type\n", + "* Meta Data (Attributes)\n", + "\n", + "We will not go into details about NetCDF basics and here are some references you might find helpful\n", + "* [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#)\n", + "* [Network Common Data Form (NetCDF)](https://www.unidata.ucar.edu/software/netcdf/) and [A Brief History of (netCDF) Time](https://www.unidata.ucar.edu/software/netcdf/time/recs.html)\n", + "\n", + "\n", + "\n", + "### Xarray essentials\n", + "Earlier we introduced the xarray pacakge and used `open_dataset` and `open_mfdataset` to retrieve NetCDF data file as an xarray Dataset object. Recall that you can review the data in a notebook by using `print(ds)` or `ds`. \n", + "\n", + "Using xarray to retrieve the aforementioned NetCDF basics is straightforwared. In general,\n", + "* Dataset: `ds`\n", + "* Data array: `ds.variables`\n", + "* Variable: `ds.ds.variables`\n", + "* Dimenssion: `ds.dims`\n", + "* Coordinate: `ds.coords`\n", + "* Data Type: `type`\n", + "* Meta Data (Attributes)\n", + "\n", + "Also, here are some references if you are new to xarray\n", + "* [Xarray in 45 minutes](https://tutorial.xarray.dev/overview/xarray-in-45-min.html)\n", + "* [Handling NetCDF Files using XArray for Absolute Beginners](https://towardsdatascience.com/handling-netcdf-files-using-xarray-for-absolute-beginners-111a8ab4463f)\n", + "\n", + "Try the following commands in action\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b3eac323", + "metadata": {}, + "outputs": [], + "source": [ + "ds = ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77ecf85d", + "metadata": {}, + "outputs": [], + "source": [ + "# Dataset \n", + "ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25e7de09", + "metadata": {}, + "outputs": [], + "source": [ + "# Data array, Variable\n", + "# Note: the info can be overwhelming. Do not attempt to grasp everything at the first glance.\n", + "ds.variables" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c41b67e", + "metadata": {}, + "outputs": [], + "source": [ + "# dimenssions\n", + "ds.dims" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "156f1dfc", + "metadata": {}, + "outputs": [], + "source": [ + "# coordinates\n", + "ds.coords" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "277d6064", + "metadata": {}, + "outputs": [], + "source": [ + "# Meta Data (Attributes)\n", + "ds.attrs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d334681f", + "metadata": {}, + "outputs": [], + "source": [ + "# type\n", + "print(type(ds))\n", + "print(type(ds.variables))\n", + "print(type(ds.dims))\n", + "print(type(ds.coords))\n", + "print(type(ds.attrs))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "643399d6", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: retrieve variable names only\n", + "list(ds.variables)" + ] + }, + { + "cell_type": "markdown", + "id": "1cf36878", + "metadata": {}, + "source": [ + "#### Discussion: variable vs. coordinates vs. dimenssions. \n", + "From [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#):\n", + "\n", + "* Dimession: A dimension may be used to represent a real physical dimension, for example, time, latitude, longitude, or height. A dimension might also be used to index other quantities, for example station or model-run-number.\n", + "A netCDF dimension has both a name and a length. A dimension length is an arbitrary positive integer, except that one dimension in a netCDF dataset can have the length UNLIMITED.\n", + "\n", + "* Variables: Variables are used to store the bulk of the data in a netCDF dataset. A variable represents an array of values of the same type. A scalar value is treated as a 0-dimensional array. A variable has a name, a data type, and a shape described by its list of dimensions specified when the variable is created. A variable may also have associated attributes, which may be added, deleted or changed after the variable is created.\n", + "\n", + "* Coordinate (Variables): It is legal for a variable to have the same name as a dimension. Such variables have no special meaning to the netCDF library. However there is a convention that such variables should be treated in a special way by software using this library.\n", + "A variable with the same name as a dimension is called a coordinate variable. It typically defines a physical coordinate corresponding to that dimension.\n", + "\n", + "\n", + "Tips: By definition, coordinates and dimenssions are also variable. For example ds.time is a coordinate but it is also a special variable. But in practice, when we talk about variable, it implies regular/non-coordinate variable. We will use this convention for the remaining notebook. " + ] + }, + { + "cell_type": "markdown", + "id": "d47120f9", + "metadata": {}, + "source": [ + "### Xarray Variable\n", + "\n", + "\n", + "We can use `ds.variables` to access varialbes of a dataset. But many times viewing print-out the whole datasets can be overwhemling. Instead, we would work on the individual variable (also called Xarray data array.) using the `ds[var_name]` syntax, where `var_name` is the name of the variable. See the following examples in action.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5a57e136", + "metadata": {}, + "outputs": [], + "source": [ + "print(type(ds[\"time\"]))\n", + "ds[\"time\"]" + ] + }, + { + "cell_type": "markdown", + "id": "51ec643e", + "metadata": {}, + "source": [ + "#### Variable properties\n", + "For individual varible, the following properties are mostly used with the assocaited xarray method to retrieve them. (assuming var = ds[var_name])\n", + "* name: `var.name`\n", + "* data content: `var.data`\n", + "* attributes: `var.attrs`\n", + "* dimenstions: `var.dims`\n", + "* data type: `var.data.dtype`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a632a525", + "metadata": {}, + "outputs": [], + "source": [ + "var_name = \"time\"\n", + "var = ds[var_name]\n", + "\n", + "print(\"var.name: \\n\", var.name, \"\\n\")\n", + "print(\"var.data: \\n\", var.data, \"\\n\")\n", + "print(\"var.attrs: \\n\", var.attrs, \"\\n\")\n", + "print(\"var.dims: \\n\", var.dims, \"\\n\")\n", + "print(\"var.data.dtype: \\n\", var.data.dtype, \"\\n\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b256b07f", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: indivdual variable info table (collect and display variable info as a tabular format)\n", + "df_info = pd.DataFrame()\n", + "df_info[\"var_name\"] = list(ds.variables)\n", + "df_info[\"dims\"] = df_info.var_name.apply(lambda x: ds[x].dims)\n", + "df_info[\"is_dim\"] = df_info.var_name.apply(lambda x: x in ds.dims)\n", + "df_info[\"n_dim\"] = df_info.var_name.apply(lambda x: len(ds[x].dims))\n", + "df_info[\"attrs\"] = df_info.var_name.apply(lambda x: ds[x].attrs)\n", + "df_info[\"dtype\"] = df_info.var_name.apply(lambda x: ds[x].data.dtype)\n", + "df_info" + ] + }, + { + "cell_type": "markdown", + "id": "e6a36b96", + "metadata": {}, + "source": [ + "### Data cleaning/Preprocessing (skipped)\n", + "Data cleaning and preprocessing is an important stage in the the data analysis pipeline. However it will be out of the scope of this notebook. For more information about data cleaning and preprocessing basics, here are some references. \n", + "* [Xarray Fundamentals](https://earth-env-data-science.github.io/lectures/xarray/xarray.html)\n", + "* [Xarray Tutorial — Pangeo Gallery documentation](http://gallery.pangeo.io/repos/pangeo-data/pangeo-tutorial-gallery/xarray.html)\n", + "* [Pythonic Data Cleaning With pandas and NumPy](https://realpython.com/python-data-cleaning-numpy-pandas/)\n", + "* [Pandas - Cleaning Data](https://www.w3schools.com/python/pandas/pandas_cleaning.asp)" + ] + }, + { + "cell_type": "markdown", + "id": "ae6e1a69", + "metadata": {}, + "source": [ + "### Plotting\n", + "Data visualization (or plotting) is another important data analysis topic and deserves its own discussion. Here in this notebook we will only demonstrate basic tool for simple data visualization tasks.\n", + "\n", + "Here are some reference you might find useful:\n", + "* xarray plotting: https://docs.xarray.dev/en/stable/user-guide/plotting.html\n", + "* Atmospheric Community Toolkit (ACT): https://arm-doe.github.io/ACT/index.html\n", + "\n", + "Note: this notebook is auto-generated using a template. It uses a general idea to select variable(s) to plot and is not customized to each indivdual notebook. Feel free to change the variables in intrrests, especially certain figure is failed to plot." + ] + }, + { + "cell_type": "markdown", + "id": "7e417fb2", + "metadata": {}, + "source": [ + "#### 1-dimenssional basic time series plot\n", + "\n", + "For the following plot we would like to find variables such that\n", + "* it has one and only one dimession\n", + "* \"time\" is its coordinate variable\n", + "* it is not a dimenssion itself,\n", + "* it is not a special variable with substrings within [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "\n", + "Please see the following example in action. (For more details about pandas filtering, please see the following references.)\n", + "* [pandas: multiple conditions while indexing data frame](https://stackoverflow.com/questions/22591174/pandas-multiple-conditions-while-indexing-data-frame-unexpected-behavior)\n", + "* [pandas.DataFrame.apply](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.apply.html)\n", + "* [How to test if a string contains one of the substrings in a list...](https://stackoverflow.com/questions/26577516/how-to-test-if-a-string-contains-one-of-the-substrings-in-a-list-in-pandas" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30edac2d", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==1, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter = df_info[(df_info.n_dim == 1) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4777a659", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_1d = df_filter.var_name.values[0]\n", + " var_1d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb5ae985", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " ds[var_1d].plot()\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "9e41066e", + "metadata": {}, + "source": [ + "#### 2-dimenssional basic plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09bd4adc", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==2, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_2 = df_info[(df_info.n_dim == 2) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2cdea1a9", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_2d = df_filter_2.var_name.values[0]\n", + " var_2d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5f48879", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " print(ds[var_2d].dims)\n", + " # ds[var_2d].plot()\n", + "\n", + " # conventionally, use \"time\" as x-axis\n", + " ds[var_2d].plot(x=\"time\")\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "7554b2a6", + "metadata": {}, + "source": [ + "#### qc-plotting (optional)\n", + "\n", + "Note: act qc plotting has more strict requirement, one of them is the associated qc variable need to have \"flag_masks\" attributes. Which is added by using ds = act.io.armfiles.read_netcdf(files_list), then ds.clean.cleanup()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71c2096f", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " ds_act = act.io.armfiles.read_netcdf(full_path)\n", + " print(type(ds_act))\n", + " ds_act.clean.cleanup()\n", + "\n", + " # or \n", + " # ds.clean.cleanup()\n", + "except Exception as e:\n", + " print(\"ERROR\", e)\n", + " ds_act = ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "844f8505", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter valid variables for ACT qc plotting\n", + "condition = (\"qc_\" + df_info.var_name).apply(lambda x: ds_act[x].attrs.get(\"flag_masks\") is not None \n", + " if x in list(ds.variables) else False)\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_3 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings))) &\n", + " condition\n", + " ]\n", + "df_filter_3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd38cc6", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "try:\n", + " qc_variable = df_filter_3.var_name.values[0]\n", + " print(qc_variable)\n", + "except Exception as e:\n", + " print(e)\n", + " \n", + "try:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + " qc_ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "d76e4d27", + "metadata": {}, + "source": [ + "#### bonus: choose variables to plot from a dropdown menu " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb733804", + "metadata": {}, + "outputs": [], + "source": [ + "# Valid variables filtering\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_4 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82d5c20d", + "metadata": {}, + "outputs": [], + "source": [ + "# example 1: using xarray plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2fc9aaa4", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# fig, ax = plt.subplots(figsize=(10, 4))\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "# fig.clear() # Remove old lines from plot and plot new one\n", + "# if len(ds[var].dims)==2:\n", + "# ds[var].plot(x=\"time\", add_colorbar=False)\n", + "# else:\n", + "# ds[var].plot()\n", + "# plt.grid()\n", + "# plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fabdd802", + "metadata": {}, + "outputs": [], + "source": [ + "# example 2: using act plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "962b4186", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "\n", + "\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "\n", + "# i_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + "# i_display.add_subplots((1,), figsize=(10, 4))\n", + "# ax = i_display.plot(var, subplot_index=(0,), set_title=f\"{var} ({ds_act[var].attrs['long_name']})\",)\n", + "\n", + "# ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "# ax.grid()\n", + "# plt.show()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/LCLHEIGHT/lcl.c1.ipynb b/VAPs/quicklook/LCLHEIGHT/lcl.c1.ipynb new file mode 100644 index 00000000..27d7893a --- /dev/null +++ b/VAPs/quicklook/LCLHEIGHT/lcl.c1.ipynb @@ -0,0 +1,339 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# LCL.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/lclheight) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'lcl'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2023-06-01', 'facility': 'C1', 'site': 'sgp', 'start_date': '2017-01-01'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2023-05-30'\n", + "date_end = '2023-06-01'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['lcl']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'temperature'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'lcl'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/LDQUANTS/LDQUANTS_tutorial.ipynb b/VAPs/quicklook/LDQUANTS/LDQUANTS_tutorial.ipynb new file mode 100644 index 00000000..d22480c3 --- /dev/null +++ b/VAPs/quicklook/LDQUANTS/LDQUANTS_tutorial.ipynb @@ -0,0 +1,838 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# LDQUANTS.C1 Notebook\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/ldquants) for more information about this vap." + ] + }, + { + "cell_type": "markdown", + "id": "97097763", + "metadata": {}, + "source": [ + "In this notebook, we demonstrate the workflow to explore ARM vap data (using ldquants as an example.) Value-added products (VAPs) are higher-order data products that have been analyzed and processed to ease scientists’ use of ARM data in atmospheric research and global climate models. \n", + "Here is the main content we will cover." + ] + }, + { + "cell_type": "markdown", + "id": "eddec40f", + "metadata": {}, + "source": [ + "# Table of Content\n", + "## Access the data\n", + "* How to retrieve the data\n", + "* Data path and file name conventions\n", + "* Load data\n", + "## Explore the data\n", + "* NetCDF Data structure\n", + "* Xarray essentials\n", + "* Xarray Variable\n", + "## Plot the data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "import random\n", + "\n", + "import glob\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "2372d5be", + "metadata": {}, + "source": [ + "## Access the data" + ] + }, + { + "cell_type": "markdown", + "id": "5b0d9684", + "metadata": {}, + "source": [ + "### How to retrieve the data\n", + "We assume the path \"/data/archive\" is available where you are running this notebook. You can use `os.path.exists(\"/data/archive\")` to verify if the path exists at your machine.\n", + "\n", + "\n", + "### Data path and file name conventions\n", + "There several common terminologies regarding ARM data, for example, data-stream-name, data-level, site, facility, instrument, etc. (For more details, please see [ARM Data File Standards Version 1.2](https://www.google.com/search?q=arm+datastream+facility+cite+definition&rlz=1C1GCEJ_enUS1029US1029&ei=hb41ZICrBPukqtsPvMODIA&ved=0ahUKEwjAgeiS0aL-AhV7kmoFHbzhAAQQ4dUDCBA&uact=5&oq=arm+datastream+facility+cite+definition&gs_lcp=Cgxnd3Mtd2l6LXNlcnAQAzIFCAAQogQyBQgAEKIEMgUIABCiBDIFCAAQogQyBQgAEKIEOgoIABBHENYEELADOgoIIRCgARDDBBAKSgQIQRgAUJ4DWIoOYO0PaAFwAXgAgAGMAYgBuQmSAQMyLjmYAQCgAQHIAQjAAQE&sclient=gws-wiz-serp).) \n", + "\n", + "For example, this notebook is called `ldquants.c1`, where `ldquants` is the \"datastream name\", and the `{process.ds_class_level}}` is the \"data level\".\n", + "\n", + "This datastream also contains site `guc` and facility `M1`. (Note: individual datastream might have multiple site-facility pairs.)\n", + "In such a case, the data of this data-stream is stored at `/data/archive/guc/gucldquantsM1.c1`, which is in the format of `//.`. We can use the following method to assign the data-stream directory `datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )`\n", + "\n", + "The data files under datastream_dir also follows naming conventions. But once reach the datastream_dir level, the most import file naming convention to differentiate the files is \"yyyyMMdd.hhmmss\", which comes handy to filter out files based on datetime. For example, we can use `glob.glob(f'{datastream_dir}/*.200709*.*')` to filter files in 2007 September.\n", + "\n", + "(Note: refer to the https://adc.arm.gov/discovery/#/ and https://adc.arm.gov/solr8/metadata/select API to explore ARM datastream and assoicated available sites and facilities\n", + "\n", + "Please see the following examples in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d7e9eb85", + "metadata": {}, + "outputs": [], + "source": [ + "# Verify if DATA_DIR path exists\n", + "DATA_DIR = \"/data/archive\"\n", + "os.path.exists(DATA_DIR)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "586993fd", + "metadata": {}, + "outputs": [], + "source": [ + "# Speicify datastream_dir following the path conventions and check its existence\n", + "DATASTREAM_NAME = \"ldquants\"\n", + "DATA_LEVEL = \"c1\"\n", + "site = \"guc\"\n", + "facility = \"M1\"\n", + "datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )\n", + "print(datastream_dir)\n", + "print(os.path.exists(datastream_dir))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0742f7c1", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: list 5 (random) files under datastream_dir\n", + "files = os.listdir(datastream_dir)\n", + "files[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39b98a36", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: get most recent file\n", + "list_of_files = glob.glob(f\"{datastream_dir}/*\") # * means all if need specific format then *.csv\n", + "latest_file = max(list_of_files, key=os.path.getctime)\n", + "latest_file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "902d514e", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus sort datastream files based on datetime\n", + "files = os.listdir(datastream_dir)\n", + "file_sorted = files.copy()\n", + "file_sorted.sort() \n", + "print(file_sorted[:5])\n", + "\n", + "# to reverse\n", + "file_sorted_reverse = files.copy() \n", + "file_sorted_reverse.sort(reverse=True)\n", + "print(file_sorted_reverse[:5])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec5923b2", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: pattern matching\n", + "# filter the 200709** files under datastream_dir\n", + "files_filter = glob.glob(f'{datastream_dir}/*.200709*.*')\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c4a4aa18", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: use armnotebook_utils.file_filter (TODO) to filter files based on datastream info \n" + ] + }, + { + "cell_type": "markdown", + "id": "cfeb9efc", + "metadata": {}, + "source": [ + "### Load data\n", + "The arm data is stored in [NetCDF](https://en.wikipedia.org/wiki/NetCDF#:~:text=%22NetCDF%20(network%20Common%20Data%20Form,format%20for%20representing%20scientific%20data.) format. We can use xarray's `open_dataset` method to load single file, or `open_mfdataset` to open multiple files. (Note: the latter will still return a single xarray dataset object by combining multiple files.)\n", + "\n", + "Note: open_dataset keeps the file handle open and lazy loads its contents. All parameters are passed directly to open_dataset. It is a preferable method over load_dataset for memory-effiency. (more details, please see [xarray.open_dataset](https://docs.xarray.dev/en/stable/generated/xarray.open_dataset.html))\n", + "\n", + "See the following example in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a440a329", + "metadata": {}, + "outputs": [], + "source": [ + "# open single file\n", + "full_path = latest_file\n", + "print(full_path)\n", + "ds_single = xr.open_dataset(full_path)\n", + "ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0143a3d", + "metadata": {}, + "outputs": [], + "source": [ + "print(ds_single)\n", + "print(type(ds_single))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c0f8939", + "metadata": {}, + "outputs": [], + "source": [ + "# open multiple files\n", + "n_files = 3\n", + "full_paths = [os.path.join(datastream_dir, f_path) for f_path in file_sorted_reverse[:n_files]]\n", + "print(full_paths)\n", + "try: # Note: sometimes multiple files cannot be merged, so we used try except here.\n", + " ds_mutiple = xr.open_mfdataset(full_paths)\n", + " # ds_mutiple\n", + " print(type(ds_mutiple))\n", + " print(ds_mutiple)\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "3fa59943", + "metadata": {}, + "source": [ + "## Explore the data" + ] + }, + { + "cell_type": "markdown", + "id": "571f69b9", + "metadata": {}, + "source": [ + "### NetCDF Data structure \n", + "\n", + "\n", + "(If you are confident with NetCDF basics and xarray essentials, feel free to skip this session.)\n", + "Before we dive into data exploration, there are some eseential concpets we should be familiar with (shown below): \n", + "* Dataset\n", + "* Data array\n", + "* Variable\n", + "* Dimenssion\n", + "* Coordinate\n", + "* Data Type\n", + "* Meta Data (Attributes)\n", + "\n", + "We will not go into details about NetCDF basics and here are some references you might find helpful\n", + "* [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#)\n", + "* [Network Common Data Form (NetCDF)](https://www.unidata.ucar.edu/software/netcdf/) and [A Brief History of (netCDF) Time](https://www.unidata.ucar.edu/software/netcdf/time/recs.html)\n", + "\n", + "\n", + "\n", + "### Xarray essentials\n", + "Earlier we introduced the xarray pacakge and used `open_dataset` and `open_mfdataset` to retrieve NetCDF data file as an xarray Dataset object. Recall that you can review the data in a notebook by using `print(ds)` or `ds`. \n", + "\n", + "Using xarray to retrieve the aforementioned NetCDF basics is straightforwared. In general,\n", + "* Dataset: `ds`\n", + "* Data array: `ds.variables`\n", + "* Variable: `ds.ds.variables`\n", + "* Dimenssion: `ds.dims`\n", + "* Coordinate: `ds.coords`\n", + "* Data Type: `type`\n", + "* Meta Data (Attributes)\n", + "\n", + "Also, here are some references if you are new to xarray\n", + "* [Xarray in 45 minutes](https://tutorial.xarray.dev/overview/xarray-in-45-min.html)\n", + "* [Handling NetCDF Files using XArray for Absolute Beginners](https://towardsdatascience.com/handling-netcdf-files-using-xarray-for-absolute-beginners-111a8ab4463f)\n", + "\n", + "Try the following commands in action\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b3eac323", + "metadata": {}, + "outputs": [], + "source": [ + "ds = ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77ecf85d", + "metadata": {}, + "outputs": [], + "source": [ + "# Dataset \n", + "ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25e7de09", + "metadata": {}, + "outputs": [], + "source": [ + "# Data array, Variable\n", + "# Note: the info can be overwhelming. Do not attempt to grasp everything at the first glance.\n", + "ds.variables" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c41b67e", + "metadata": {}, + "outputs": [], + "source": [ + "# dimenssions\n", + "ds.dims" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "156f1dfc", + "metadata": {}, + "outputs": [], + "source": [ + "# coordinates\n", + "ds.coords" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "277d6064", + "metadata": {}, + "outputs": [], + "source": [ + "# Meta Data (Attributes)\n", + "ds.attrs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d334681f", + "metadata": {}, + "outputs": [], + "source": [ + "# type\n", + "print(type(ds))\n", + "print(type(ds.variables))\n", + "print(type(ds.dims))\n", + "print(type(ds.coords))\n", + "print(type(ds.attrs))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "643399d6", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: retrieve variable names only\n", + "list(ds.variables)" + ] + }, + { + "cell_type": "markdown", + "id": "1cf36878", + "metadata": {}, + "source": [ + "#### Discussion: variable vs. coordinates vs. dimenssions. \n", + "From [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#):\n", + "\n", + "* Dimession: A dimension may be used to represent a real physical dimension, for example, time, latitude, longitude, or height. A dimension might also be used to index other quantities, for example station or model-run-number.\n", + "A netCDF dimension has both a name and a length. A dimension length is an arbitrary positive integer, except that one dimension in a netCDF dataset can have the length UNLIMITED.\n", + "\n", + "* Variables: Variables are used to store the bulk of the data in a netCDF dataset. A variable represents an array of values of the same type. A scalar value is treated as a 0-dimensional array. A variable has a name, a data type, and a shape described by its list of dimensions specified when the variable is created. A variable may also have associated attributes, which may be added, deleted or changed after the variable is created.\n", + "\n", + "* Coordinate (Variables): It is legal for a variable to have the same name as a dimension. Such variables have no special meaning to the netCDF library. However there is a convention that such variables should be treated in a special way by software using this library.\n", + "A variable with the same name as a dimension is called a coordinate variable. It typically defines a physical coordinate corresponding to that dimension.\n", + "\n", + "\n", + "Tips: By definition, coordinates and dimenssions are also variable. For example ds.time is a coordinate but it is also a special variable. But in practice, when we talk about variable, it implies regular/non-coordinate variable. We will use this convention for the remaining notebook. " + ] + }, + { + "cell_type": "markdown", + "id": "d47120f9", + "metadata": {}, + "source": [ + "### Xarray Variable\n", + "\n", + "\n", + "We can use `ds.variables` to access varialbes of a dataset. But many times viewing print-out the whole datasets can be overwhemling. Instead, we would work on the individual variable (also called Xarray data array.) using the `ds[var_name]` syntax, where `var_name` is the name of the variable. See the following examples in action.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5a57e136", + "metadata": {}, + "outputs": [], + "source": [ + "print(type(ds[\"time\"]))\n", + "ds[\"time\"]" + ] + }, + { + "cell_type": "markdown", + "id": "51ec643e", + "metadata": {}, + "source": [ + "#### Variable properties\n", + "For individual varible, the following properties are mostly used with the assocaited xarray method to retrieve them. (assuming var = ds[var_name])\n", + "* name: `var.name`\n", + "* data content: `var.data`\n", + "* attributes: `var.attrs`\n", + "* dimenstions: `var.dims`\n", + "* data type: `var.data.dtype`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a632a525", + "metadata": {}, + "outputs": [], + "source": [ + "var_name = \"time\"\n", + "var = ds[var_name]\n", + "\n", + "print(\"var.name: \\n\", var.name, \"\\n\")\n", + "print(\"var.data: \\n\", var.data, \"\\n\")\n", + "print(\"var.attrs: \\n\", var.attrs, \"\\n\")\n", + "print(\"var.dims: \\n\", var.dims, \"\\n\")\n", + "print(\"var.data.dtype: \\n\", var.data.dtype, \"\\n\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b256b07f", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: indivdual variable info table (collect and display variable info as a tabular format)\n", + "df_info = pd.DataFrame()\n", + "df_info[\"var_name\"] = list(ds.variables)\n", + "df_info[\"dims\"] = df_info.var_name.apply(lambda x: ds[x].dims)\n", + "df_info[\"is_dim\"] = df_info.var_name.apply(lambda x: x in ds.dims)\n", + "df_info[\"n_dim\"] = df_info.var_name.apply(lambda x: len(ds[x].dims))\n", + "df_info[\"attrs\"] = df_info.var_name.apply(lambda x: ds[x].attrs)\n", + "df_info[\"dtype\"] = df_info.var_name.apply(lambda x: ds[x].data.dtype)\n", + "df_info" + ] + }, + { + "cell_type": "markdown", + "id": "e6a36b96", + "metadata": {}, + "source": [ + "### Data cleaning/Preprocessing (skipped)\n", + "Data cleaning and preprocessing is an important stage in the the data analysis pipeline. However it will be out of the scope of this notebook. For more information about data cleaning and preprocessing basics, here are some references. \n", + "* [Xarray Fundamentals](https://earth-env-data-science.github.io/lectures/xarray/xarray.html)\n", + "* [Xarray Tutorial — Pangeo Gallery documentation](http://gallery.pangeo.io/repos/pangeo-data/pangeo-tutorial-gallery/xarray.html)\n", + "* [Pythonic Data Cleaning With pandas and NumPy](https://realpython.com/python-data-cleaning-numpy-pandas/)\n", + "* [Pandas - Cleaning Data](https://www.w3schools.com/python/pandas/pandas_cleaning.asp)" + ] + }, + { + "cell_type": "markdown", + "id": "ae6e1a69", + "metadata": {}, + "source": [ + "### Plotting\n", + "Data visualization (or plotting) is another important data analysis topic and deserves its own discussion. Here in this notebook we will only demonstrate basic tool for simple data visualization tasks.\n", + "\n", + "Here are some reference you might find useful:\n", + "* xarray plotting: https://docs.xarray.dev/en/stable/user-guide/plotting.html\n", + "* Atmospheric Community Toolkit (ACT): https://arm-doe.github.io/ACT/index.html\n", + "\n", + "Note: this notebook is auto-generated using a template. It uses a general idea to select variable(s) to plot and is not customized to each indivdual notebook. Feel free to change the variables in intrrests, especially certain figure is failed to plot." + ] + }, + { + "cell_type": "markdown", + "id": "7e417fb2", + "metadata": {}, + "source": [ + "#### 1-dimenssional basic time series plot\n", + "\n", + "For the following plot we would like to find variables such that\n", + "* it has one and only one dimession\n", + "* \"time\" is its coordinate variable\n", + "* it is not a dimenssion itself,\n", + "* it is not a special variable with substrings within [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "\n", + "Please see the following example in action. (For more details about pandas filtering, please see the following references.)\n", + "* [pandas: multiple conditions while indexing data frame](https://stackoverflow.com/questions/22591174/pandas-multiple-conditions-while-indexing-data-frame-unexpected-behavior)\n", + "* [pandas.DataFrame.apply](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.apply.html)\n", + "* [How to test if a string contains one of the substrings in a list...](https://stackoverflow.com/questions/26577516/how-to-test-if-a-string-contains-one-of-the-substrings-in-a-list-in-pandas" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30edac2d", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==1, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter = df_info[(df_info.n_dim == 1) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4777a659", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_1d = df_filter.var_name.values[0]\n", + " var_1d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb5ae985", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " ds[var_1d].plot()\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "9e41066e", + "metadata": {}, + "source": [ + "#### 2-dimenssional basic plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09bd4adc", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==2, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_2 = df_info[(df_info.n_dim == 2) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2cdea1a9", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_2d = df_filter_2.var_name.values[0]\n", + " var_2d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5f48879", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " print(ds[var_2d].dims)\n", + " # ds[var_2d].plot()\n", + "\n", + " # conventionally, use \"time\" as x-axis\n", + " ds[var_2d].plot(x=\"time\")\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "7554b2a6", + "metadata": {}, + "source": [ + "#### qc-plotting (optional)\n", + "\n", + "Note: act qc plotting has more strict requirement, one of them is the associated qc variable need to have \"flag_masks\" attributes. Which is added by using ds = act.io.armfiles.read_netcdf(files_list), then ds.clean.cleanup()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71c2096f", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " ds_act = act.io.armfiles.read_netcdf(full_path)\n", + " print(type(ds_act))\n", + " ds_act.clean.cleanup()\n", + "\n", + " # or \n", + " # ds.clean.cleanup()\n", + "except Exception as e:\n", + " print(\"ERROR\", e)\n", + " ds_act = ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "844f8505", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter valid variables for ACT qc plotting\n", + "condition = (\"qc_\" + df_info.var_name).apply(lambda x: ds_act[x].attrs.get(\"flag_masks\") is not None \n", + " if x in list(ds.variables) else False)\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_3 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings))) &\n", + " condition\n", + " ]\n", + "df_filter_3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd38cc6", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "try:\n", + " qc_variable = df_filter_3.var_name.values[0]\n", + " print(qc_variable)\n", + "except Exception as e:\n", + " print(e)\n", + " \n", + "try:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + " qc_ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "d76e4d27", + "metadata": {}, + "source": [ + "#### bonus: choose variables to plot from a dropdown menu " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb733804", + "metadata": {}, + "outputs": [], + "source": [ + "# Valid variables filtering\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_4 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82d5c20d", + "metadata": {}, + "outputs": [], + "source": [ + "# example 1: using xarray plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2fc9aaa4", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# fig, ax = plt.subplots(figsize=(10, 4))\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "# fig.clear() # Remove old lines from plot and plot new one\n", + "# if len(ds[var].dims)==2:\n", + "# ds[var].plot(x=\"time\", add_colorbar=False)\n", + "# else:\n", + "# ds[var].plot()\n", + "# plt.grid()\n", + "# plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fabdd802", + "metadata": {}, + "outputs": [], + "source": [ + "# example 2: using act plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "962b4186", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "\n", + "\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "\n", + "# i_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + "# i_display.add_subplots((1,), figsize=(10, 4))\n", + "# ax = i_display.plot(var, subplot_index=(0,), set_title=f\"{var} ({ds_act[var].attrs['long_name']})\",)\n", + "\n", + "# ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "# ax.grid()\n", + "# plt.show()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/LDQUANTS/ldquants.c1.ipynb b/VAPs/quicklook/LDQUANTS/ldquants.c1.ipynb new file mode 100644 index 00000000..b34c8218 --- /dev/null +++ b/VAPs/quicklook/LDQUANTS/ldquants.c1.ipynb @@ -0,0 +1,297 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# LDQUANTS.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/ldquants) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'ldquants'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2023-06-15', 'facility': 'M1', 'site': 'guc', 'start_date': '2021-09-01'}, {'end_date': '2023-06-15', 'facility': 'S2', 'site': 'guc', 'start_date': '2021-09-03'}, {'end_date': '2023-12-18', 'facility': 'C1', 'site': 'ena', 'start_date': '2014-02-27'}, {'end_date': '2022-09-30', 'facility': 'M1', 'site': 'hou', 'start_date': '2021-08-05'}, {'end_date': '2022-10-01', 'facility': 'S1', 'site': 'hou', 'start_date': '2021-08-18'}, {'end_date': '2022-05-12', 'facility': 'S2', 'site': 'hou', 'start_date': '2022-04-12'}, {'end_date': '2022-09-30', 'facility': 'S3', 'site': 'hou', 'start_date': '2022-05-05'}, {'end_date': '2023-12-10', 'facility': 'M1', 'site': 'epc', 'start_date': '2023-01-15'}, {'end_date': '2023-12-10', 'facility': 'S2', 'site': 'epc', 'start_date': '2023-01-15'}, {'end_date': '2015-12-01', 'facility': 'S10', 'site': 'mao', 'start_date': '2014-09-24'}, {'end_date': '2023-12-18', 'facility': 'C1', 'site': 'sgp', 'start_date': '2016-11-02'}, {'end_date': '2023-09-28', 'facility': 'E13', 'site': 'sgp', 'start_date': '2016-11-04'}, {'end_date': '2023-09-28', 'facility': 'I10', 'site': 'sgp', 'start_date': '2016-11-28'}, {'end_date': '2023-09-28', 'facility': 'I8', 'site': 'sgp', 'start_date': '2016-12-05'}, {'end_date': '2023-09-28', 'facility': 'I9', 'site': 'sgp', 'start_date': '2016-11-28'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2023-12-16'\n", + "date_end = '2023-12-18'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['rain_rate', 'reflectivity_factor_sband20c', 'reflectivity_factor_cband20c']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'rain_rate'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/LSSONDE/LSSONDE_tutorial.ipynb b/VAPs/quicklook/LSSONDE/LSSONDE_tutorial.ipynb new file mode 100644 index 00000000..9d53a719 --- /dev/null +++ b/VAPs/quicklook/LSSONDE/LSSONDE_tutorial.ipynb @@ -0,0 +1,838 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# LSSONDE.C1 Notebook\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/lssonde) for more information about this vap." + ] + }, + { + "cell_type": "markdown", + "id": "97097763", + "metadata": {}, + "source": [ + "In this notebook, we demonstrate the workflow to explore ARM vap data (using lssonde as an example.) Value-added products (VAPs) are higher-order data products that have been analyzed and processed to ease scientists’ use of ARM data in atmospheric research and global climate models. \n", + "Here is the main content we will cover." + ] + }, + { + "cell_type": "markdown", + "id": "eddec40f", + "metadata": {}, + "source": [ + "# Table of Content\n", + "## Access the data\n", + "* How to retrieve the data\n", + "* Data path and file name conventions\n", + "* Load data\n", + "## Explore the data\n", + "* NetCDF Data structure\n", + "* Xarray essentials\n", + "* Xarray Variable\n", + "## Plot the data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "import random\n", + "\n", + "import glob\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "2372d5be", + "metadata": {}, + "source": [ + "## Access the data" + ] + }, + { + "cell_type": "markdown", + "id": "5b0d9684", + "metadata": {}, + "source": [ + "### How to retrieve the data\n", + "We assume the path \"/data/archive\" is available where you are running this notebook. You can use `os.path.exists(\"/data/archive\")` to verify if the path exists at your machine.\n", + "\n", + "\n", + "### Data path and file name conventions\n", + "There several common terminologies regarding ARM data, for example, data-stream-name, data-level, site, facility, instrument, etc. (For more details, please see [ARM Data File Standards Version 1.2](https://www.google.com/search?q=arm+datastream+facility+cite+definition&rlz=1C1GCEJ_enUS1029US1029&ei=hb41ZICrBPukqtsPvMODIA&ved=0ahUKEwjAgeiS0aL-AhV7kmoFHbzhAAQQ4dUDCBA&uact=5&oq=arm+datastream+facility+cite+definition&gs_lcp=Cgxnd3Mtd2l6LXNlcnAQAzIFCAAQogQyBQgAEKIEMgUIABCiBDIFCAAQogQyBQgAEKIEOgoIABBHENYEELADOgoIIRCgARDDBBAKSgQIQRgAUJ4DWIoOYO0PaAFwAXgAgAGMAYgBuQmSAQMyLjmYAQCgAQHIAQjAAQE&sclient=gws-wiz-serp).) \n", + "\n", + "For example, this notebook is called `lssonde.c1`, where `lssonde` is the \"datastream name\", and the `{process.ds_class_level}}` is the \"data level\".\n", + "\n", + "This datastream also contains site `acx` and facility `M1`. (Note: individual datastream might have multiple site-facility pairs.)\n", + "In such a case, the data of this data-stream is stored at `/data/archive/acx/acxlssondeM1.c1`, which is in the format of `//.`. We can use the following method to assign the data-stream directory `datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )`\n", + "\n", + "The data files under datastream_dir also follows naming conventions. But once reach the datastream_dir level, the most import file naming convention to differentiate the files is \"yyyyMMdd.hhmmss\", which comes handy to filter out files based on datetime. For example, we can use `glob.glob(f'{datastream_dir}/*.200709*.*')` to filter files in 2007 September.\n", + "\n", + "(Note: refer to the https://adc.arm.gov/discovery/#/ and https://adc.arm.gov/solr8/metadata/select API to explore ARM datastream and assoicated available sites and facilities\n", + "\n", + "Please see the following examples in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d7e9eb85", + "metadata": {}, + "outputs": [], + "source": [ + "# Verify if DATA_DIR path exists\n", + "DATA_DIR = \"/data/archive\"\n", + "os.path.exists(DATA_DIR)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "586993fd", + "metadata": {}, + "outputs": [], + "source": [ + "# Speicify datastream_dir following the path conventions and check its existence\n", + "DATASTREAM_NAME = \"lssonde\"\n", + "DATA_LEVEL = \"c1\"\n", + "site = \"acx\"\n", + "facility = \"M1\"\n", + "datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )\n", + "print(datastream_dir)\n", + "print(os.path.exists(datastream_dir))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0742f7c1", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: list 5 (random) files under datastream_dir\n", + "files = os.listdir(datastream_dir)\n", + "files[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39b98a36", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: get most recent file\n", + "list_of_files = glob.glob(f\"{datastream_dir}/*\") # * means all if need specific format then *.csv\n", + "latest_file = max(list_of_files, key=os.path.getctime)\n", + "latest_file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "902d514e", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus sort datastream files based on datetime\n", + "files = os.listdir(datastream_dir)\n", + "file_sorted = files.copy()\n", + "file_sorted.sort() \n", + "print(file_sorted[:5])\n", + "\n", + "# to reverse\n", + "file_sorted_reverse = files.copy() \n", + "file_sorted_reverse.sort(reverse=True)\n", + "print(file_sorted_reverse[:5])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec5923b2", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: pattern matching\n", + "# filter the 200709** files under datastream_dir\n", + "files_filter = glob.glob(f'{datastream_dir}/*.200709*.*')\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c4a4aa18", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: use armnotebook_utils.file_filter (TODO) to filter files based on datastream info \n" + ] + }, + { + "cell_type": "markdown", + "id": "cfeb9efc", + "metadata": {}, + "source": [ + "### Load data\n", + "The arm data is stored in [NetCDF](https://en.wikipedia.org/wiki/NetCDF#:~:text=%22NetCDF%20(network%20Common%20Data%20Form,format%20for%20representing%20scientific%20data.) format. We can use xarray's `open_dataset` method to load single file, or `open_mfdataset` to open multiple files. (Note: the latter will still return a single xarray dataset object by combining multiple files.)\n", + "\n", + "Note: open_dataset keeps the file handle open and lazy loads its contents. All parameters are passed directly to open_dataset. It is a preferable method over load_dataset for memory-effiency. (more details, please see [xarray.open_dataset](https://docs.xarray.dev/en/stable/generated/xarray.open_dataset.html))\n", + "\n", + "See the following example in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a440a329", + "metadata": {}, + "outputs": [], + "source": [ + "# open single file\n", + "full_path = latest_file\n", + "print(full_path)\n", + "ds_single = xr.open_dataset(full_path)\n", + "ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0143a3d", + "metadata": {}, + "outputs": [], + "source": [ + "print(ds_single)\n", + "print(type(ds_single))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c0f8939", + "metadata": {}, + "outputs": [], + "source": [ + "# open multiple files\n", + "n_files = 3\n", + "full_paths = [os.path.join(datastream_dir, f_path) for f_path in file_sorted_reverse[:n_files]]\n", + "print(full_paths)\n", + "try: # Note: sometimes multiple files cannot be merged, so we used try except here.\n", + " ds_mutiple = xr.open_mfdataset(full_paths)\n", + " # ds_mutiple\n", + " print(type(ds_mutiple))\n", + " print(ds_mutiple)\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "3fa59943", + "metadata": {}, + "source": [ + "## Explore the data" + ] + }, + { + "cell_type": "markdown", + "id": "571f69b9", + "metadata": {}, + "source": [ + "### NetCDF Data structure \n", + "\n", + "\n", + "(If you are confident with NetCDF basics and xarray essentials, feel free to skip this session.)\n", + "Before we dive into data exploration, there are some eseential concpets we should be familiar with (shown below): \n", + "* Dataset\n", + "* Data array\n", + "* Variable\n", + "* Dimenssion\n", + "* Coordinate\n", + "* Data Type\n", + "* Meta Data (Attributes)\n", + "\n", + "We will not go into details about NetCDF basics and here are some references you might find helpful\n", + "* [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#)\n", + "* [Network Common Data Form (NetCDF)](https://www.unidata.ucar.edu/software/netcdf/) and [A Brief History of (netCDF) Time](https://www.unidata.ucar.edu/software/netcdf/time/recs.html)\n", + "\n", + "\n", + "\n", + "### Xarray essentials\n", + "Earlier we introduced the xarray pacakge and used `open_dataset` and `open_mfdataset` to retrieve NetCDF data file as an xarray Dataset object. Recall that you can review the data in a notebook by using `print(ds)` or `ds`. \n", + "\n", + "Using xarray to retrieve the aforementioned NetCDF basics is straightforwared. In general,\n", + "* Dataset: `ds`\n", + "* Data array: `ds.variables`\n", + "* Variable: `ds.ds.variables`\n", + "* Dimenssion: `ds.dims`\n", + "* Coordinate: `ds.coords`\n", + "* Data Type: `type`\n", + "* Meta Data (Attributes)\n", + "\n", + "Also, here are some references if you are new to xarray\n", + "* [Xarray in 45 minutes](https://tutorial.xarray.dev/overview/xarray-in-45-min.html)\n", + "* [Handling NetCDF Files using XArray for Absolute Beginners](https://towardsdatascience.com/handling-netcdf-files-using-xarray-for-absolute-beginners-111a8ab4463f)\n", + "\n", + "Try the following commands in action\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b3eac323", + "metadata": {}, + "outputs": [], + "source": [ + "ds = ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77ecf85d", + "metadata": {}, + "outputs": [], + "source": [ + "# Dataset \n", + "ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25e7de09", + "metadata": {}, + "outputs": [], + "source": [ + "# Data array, Variable\n", + "# Note: the info can be overwhelming. Do not attempt to grasp everything at the first glance.\n", + "ds.variables" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c41b67e", + "metadata": {}, + "outputs": [], + "source": [ + "# dimenssions\n", + "ds.dims" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "156f1dfc", + "metadata": {}, + "outputs": [], + "source": [ + "# coordinates\n", + "ds.coords" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "277d6064", + "metadata": {}, + "outputs": [], + "source": [ + "# Meta Data (Attributes)\n", + "ds.attrs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d334681f", + "metadata": {}, + "outputs": [], + "source": [ + "# type\n", + "print(type(ds))\n", + "print(type(ds.variables))\n", + "print(type(ds.dims))\n", + "print(type(ds.coords))\n", + "print(type(ds.attrs))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "643399d6", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: retrieve variable names only\n", + "list(ds.variables)" + ] + }, + { + "cell_type": "markdown", + "id": "1cf36878", + "metadata": {}, + "source": [ + "#### Discussion: variable vs. coordinates vs. dimenssions. \n", + "From [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#):\n", + "\n", + "* Dimession: A dimension may be used to represent a real physical dimension, for example, time, latitude, longitude, or height. A dimension might also be used to index other quantities, for example station or model-run-number.\n", + "A netCDF dimension has both a name and a length. A dimension length is an arbitrary positive integer, except that one dimension in a netCDF dataset can have the length UNLIMITED.\n", + "\n", + "* Variables: Variables are used to store the bulk of the data in a netCDF dataset. A variable represents an array of values of the same type. A scalar value is treated as a 0-dimensional array. A variable has a name, a data type, and a shape described by its list of dimensions specified when the variable is created. A variable may also have associated attributes, which may be added, deleted or changed after the variable is created.\n", + "\n", + "* Coordinate (Variables): It is legal for a variable to have the same name as a dimension. Such variables have no special meaning to the netCDF library. However there is a convention that such variables should be treated in a special way by software using this library.\n", + "A variable with the same name as a dimension is called a coordinate variable. It typically defines a physical coordinate corresponding to that dimension.\n", + "\n", + "\n", + "Tips: By definition, coordinates and dimenssions are also variable. For example ds.time is a coordinate but it is also a special variable. But in practice, when we talk about variable, it implies regular/non-coordinate variable. We will use this convention for the remaining notebook. " + ] + }, + { + "cell_type": "markdown", + "id": "d47120f9", + "metadata": {}, + "source": [ + "### Xarray Variable\n", + "\n", + "\n", + "We can use `ds.variables` to access varialbes of a dataset. But many times viewing print-out the whole datasets can be overwhemling. Instead, we would work on the individual variable (also called Xarray data array.) using the `ds[var_name]` syntax, where `var_name` is the name of the variable. See the following examples in action.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5a57e136", + "metadata": {}, + "outputs": [], + "source": [ + "print(type(ds[\"time\"]))\n", + "ds[\"time\"]" + ] + }, + { + "cell_type": "markdown", + "id": "51ec643e", + "metadata": {}, + "source": [ + "#### Variable properties\n", + "For individual varible, the following properties are mostly used with the assocaited xarray method to retrieve them. (assuming var = ds[var_name])\n", + "* name: `var.name`\n", + "* data content: `var.data`\n", + "* attributes: `var.attrs`\n", + "* dimenstions: `var.dims`\n", + "* data type: `var.data.dtype`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a632a525", + "metadata": {}, + "outputs": [], + "source": [ + "var_name = \"time\"\n", + "var = ds[var_name]\n", + "\n", + "print(\"var.name: \\n\", var.name, \"\\n\")\n", + "print(\"var.data: \\n\", var.data, \"\\n\")\n", + "print(\"var.attrs: \\n\", var.attrs, \"\\n\")\n", + "print(\"var.dims: \\n\", var.dims, \"\\n\")\n", + "print(\"var.data.dtype: \\n\", var.data.dtype, \"\\n\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b256b07f", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: indivdual variable info table (collect and display variable info as a tabular format)\n", + "df_info = pd.DataFrame()\n", + "df_info[\"var_name\"] = list(ds.variables)\n", + "df_info[\"dims\"] = df_info.var_name.apply(lambda x: ds[x].dims)\n", + "df_info[\"is_dim\"] = df_info.var_name.apply(lambda x: x in ds.dims)\n", + "df_info[\"n_dim\"] = df_info.var_name.apply(lambda x: len(ds[x].dims))\n", + "df_info[\"attrs\"] = df_info.var_name.apply(lambda x: ds[x].attrs)\n", + "df_info[\"dtype\"] = df_info.var_name.apply(lambda x: ds[x].data.dtype)\n", + "df_info" + ] + }, + { + "cell_type": "markdown", + "id": "e6a36b96", + "metadata": {}, + "source": [ + "### Data cleaning/Preprocessing (skipped)\n", + "Data cleaning and preprocessing is an important stage in the the data analysis pipeline. However it will be out of the scope of this notebook. For more information about data cleaning and preprocessing basics, here are some references. \n", + "* [Xarray Fundamentals](https://earth-env-data-science.github.io/lectures/xarray/xarray.html)\n", + "* [Xarray Tutorial — Pangeo Gallery documentation](http://gallery.pangeo.io/repos/pangeo-data/pangeo-tutorial-gallery/xarray.html)\n", + "* [Pythonic Data Cleaning With pandas and NumPy](https://realpython.com/python-data-cleaning-numpy-pandas/)\n", + "* [Pandas - Cleaning Data](https://www.w3schools.com/python/pandas/pandas_cleaning.asp)" + ] + }, + { + "cell_type": "markdown", + "id": "ae6e1a69", + "metadata": {}, + "source": [ + "### Plotting\n", + "Data visualization (or plotting) is another important data analysis topic and deserves its own discussion. Here in this notebook we will only demonstrate basic tool for simple data visualization tasks.\n", + "\n", + "Here are some reference you might find useful:\n", + "* xarray plotting: https://docs.xarray.dev/en/stable/user-guide/plotting.html\n", + "* Atmospheric Community Toolkit (ACT): https://arm-doe.github.io/ACT/index.html\n", + "\n", + "Note: this notebook is auto-generated using a template. It uses a general idea to select variable(s) to plot and is not customized to each indivdual notebook. Feel free to change the variables in intrrests, especially certain figure is failed to plot." + ] + }, + { + "cell_type": "markdown", + "id": "7e417fb2", + "metadata": {}, + "source": [ + "#### 1-dimenssional basic time series plot\n", + "\n", + "For the following plot we would like to find variables such that\n", + "* it has one and only one dimession\n", + "* \"time\" is its coordinate variable\n", + "* it is not a dimenssion itself,\n", + "* it is not a special variable with substrings within [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "\n", + "Please see the following example in action. (For more details about pandas filtering, please see the following references.)\n", + "* [pandas: multiple conditions while indexing data frame](https://stackoverflow.com/questions/22591174/pandas-multiple-conditions-while-indexing-data-frame-unexpected-behavior)\n", + "* [pandas.DataFrame.apply](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.apply.html)\n", + "* [How to test if a string contains one of the substrings in a list...](https://stackoverflow.com/questions/26577516/how-to-test-if-a-string-contains-one-of-the-substrings-in-a-list-in-pandas" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30edac2d", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==1, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter = df_info[(df_info.n_dim == 1) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4777a659", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_1d = df_filter.var_name.values[0]\n", + " var_1d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb5ae985", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " ds[var_1d].plot()\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "9e41066e", + "metadata": {}, + "source": [ + "#### 2-dimenssional basic plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09bd4adc", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==2, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_2 = df_info[(df_info.n_dim == 2) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2cdea1a9", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_2d = df_filter_2.var_name.values[0]\n", + " var_2d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5f48879", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " print(ds[var_2d].dims)\n", + " # ds[var_2d].plot()\n", + "\n", + " # conventionally, use \"time\" as x-axis\n", + " ds[var_2d].plot(x=\"time\")\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "7554b2a6", + "metadata": {}, + "source": [ + "#### qc-plotting (optional)\n", + "\n", + "Note: act qc plotting has more strict requirement, one of them is the associated qc variable need to have \"flag_masks\" attributes. Which is added by using ds = act.io.armfiles.read_netcdf(files_list), then ds.clean.cleanup()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71c2096f", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " ds_act = act.io.armfiles.read_netcdf(full_path)\n", + " print(type(ds_act))\n", + " ds_act.clean.cleanup()\n", + "\n", + " # or \n", + " # ds.clean.cleanup()\n", + "except Exception as e:\n", + " print(\"ERROR\", e)\n", + " ds_act = ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "844f8505", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter valid variables for ACT qc plotting\n", + "condition = (\"qc_\" + df_info.var_name).apply(lambda x: ds_act[x].attrs.get(\"flag_masks\") is not None \n", + " if x in list(ds.variables) else False)\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_3 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings))) &\n", + " condition\n", + " ]\n", + "df_filter_3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd38cc6", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "try:\n", + " qc_variable = df_filter_3.var_name.values[0]\n", + " print(qc_variable)\n", + "except Exception as e:\n", + " print(e)\n", + " \n", + "try:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + " qc_ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "d76e4d27", + "metadata": {}, + "source": [ + "#### bonus: choose variables to plot from a dropdown menu " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb733804", + "metadata": {}, + "outputs": [], + "source": [ + "# Valid variables filtering\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_4 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82d5c20d", + "metadata": {}, + "outputs": [], + "source": [ + "# example 1: using xarray plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2fc9aaa4", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# fig, ax = plt.subplots(figsize=(10, 4))\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "# fig.clear() # Remove old lines from plot and plot new one\n", + "# if len(ds[var].dims)==2:\n", + "# ds[var].plot(x=\"time\", add_colorbar=False)\n", + "# else:\n", + "# ds[var].plot()\n", + "# plt.grid()\n", + "# plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fabdd802", + "metadata": {}, + "outputs": [], + "source": [ + "# example 2: using act plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "962b4186", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "\n", + "\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "\n", + "# i_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + "# i_display.add_subplots((1,), figsize=(10, 4))\n", + "# ax = i_display.plot(var, subplot_index=(0,), set_title=f\"{var} ({ds_act[var].attrs['long_name']})\",)\n", + "\n", + "# ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "# ax.grid()\n", + "# plt.show()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/LSSONDE/lssonde.c1.ipynb b/VAPs/quicklook/LSSONDE/lssonde.c1.ipynb new file mode 100644 index 00000000..3a9c6d6c --- /dev/null +++ b/VAPs/quicklook/LSSONDE/lssonde.c1.ipynb @@ -0,0 +1,297 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# LSSONDE.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/lssonde) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'lssonde'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2015-02-10', 'facility': 'M1', 'site': 'acx', 'start_date': '2015-01-12'}, {'end_date': '2000-10-08', 'facility': 'B1', 'site': 'sgp', 'start_date': '1997-06-16'}, {'end_date': '2000-10-08', 'facility': 'B4', 'site': 'sgp', 'start_date': '1997-06-16'}, {'end_date': '2000-10-08', 'facility': 'B5', 'site': 'sgp', 'start_date': '1997-06-16'}, {'end_date': '2000-10-05', 'facility': 'B6', 'site': 'sgp', 'start_date': '1997-06-16'}, {'end_date': '2023-12-18', 'facility': 'C1', 'site': 'sgp', 'start_date': '1994-04-11'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'B1' )\n", + "\n", + "date_start = '2000-10-07'\n", + "date_end = '2000-10-08'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['pres', 'tdry', 'rh']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],linestyle='None')\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'pres'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],linestyle='None')\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],linestyle='None')\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/MASCPARTICLES/MASCPARTICLES_tutorial.ipynb b/VAPs/quicklook/MASCPARTICLES/MASCPARTICLES_tutorial.ipynb new file mode 100644 index 00000000..1b42502a --- /dev/null +++ b/VAPs/quicklook/MASCPARTICLES/MASCPARTICLES_tutorial.ipynb @@ -0,0 +1,838 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# MASCPARTICLES.C1 Notebook\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/mascparticles) for more information about this vap." + ] + }, + { + "cell_type": "markdown", + "id": "97097763", + "metadata": {}, + "source": [ + "In this notebook, we demonstrate the workflow to explore ARM vap data (using mascparticles as an example.) Value-added products (VAPs) are higher-order data products that have been analyzed and processed to ease scientists’ use of ARM data in atmospheric research and global climate models. \n", + "Here is the main content we will cover." + ] + }, + { + "cell_type": "markdown", + "id": "eddec40f", + "metadata": {}, + "source": [ + "# Table of Content\n", + "## Access the data\n", + "* How to retrieve the data\n", + "* Data path and file name conventions\n", + "* Load data\n", + "## Explore the data\n", + "* NetCDF Data structure\n", + "* Xarray essentials\n", + "* Xarray Variable\n", + "## Plot the data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "import random\n", + "\n", + "import glob\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "2372d5be", + "metadata": {}, + "source": [ + "## Access the data" + ] + }, + { + "cell_type": "markdown", + "id": "5b0d9684", + "metadata": {}, + "source": [ + "### How to retrieve the data\n", + "We assume the path \"/data/archive\" is available where you are running this notebook. You can use `os.path.exists(\"/data/archive\")` to verify if the path exists at your machine.\n", + "\n", + "\n", + "### Data path and file name conventions\n", + "There several common terminologies regarding ARM data, for example, data-stream-name, data-level, site, facility, instrument, etc. (For more details, please see [ARM Data File Standards Version 1.2](https://www.google.com/search?q=arm+datastream+facility+cite+definition&rlz=1C1GCEJ_enUS1029US1029&ei=hb41ZICrBPukqtsPvMODIA&ved=0ahUKEwjAgeiS0aL-AhV7kmoFHbzhAAQQ4dUDCBA&uact=5&oq=arm+datastream+facility+cite+definition&gs_lcp=Cgxnd3Mtd2l6LXNlcnAQAzIFCAAQogQyBQgAEKIEMgUIABCiBDIFCAAQogQyBQgAEKIEOgoIABBHENYEELADOgoIIRCgARDDBBAKSgQIQRgAUJ4DWIoOYO0PaAFwAXgAgAGMAYgBuQmSAQMyLjmYAQCgAQHIAQjAAQE&sclient=gws-wiz-serp).) \n", + "\n", + "For example, this notebook is called `mascparticles.c1`, where `mascparticles` is the \"datastream name\", and the `{process.ds_class_level}}` is the \"data level\".\n", + "\n", + "This datastream also contains site `oli` and facility `M1`. (Note: individual datastream might have multiple site-facility pairs.)\n", + "In such a case, the data of this data-stream is stored at `/data/archive/oli/olimascparticlesM1.c1`, which is in the format of `//.`. We can use the following method to assign the data-stream directory `datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )`\n", + "\n", + "The data files under datastream_dir also follows naming conventions. But once reach the datastream_dir level, the most import file naming convention to differentiate the files is \"yyyyMMdd.hhmmss\", which comes handy to filter out files based on datetime. For example, we can use `glob.glob(f'{datastream_dir}/*.200709*.*')` to filter files in 2007 September.\n", + "\n", + "(Note: refer to the https://adc.arm.gov/discovery/#/ and https://adc.arm.gov/solr8/metadata/select API to explore ARM datastream and assoicated available sites and facilities\n", + "\n", + "Please see the following examples in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d7e9eb85", + "metadata": {}, + "outputs": [], + "source": [ + "# Verify if DATA_DIR path exists\n", + "DATA_DIR = \"/data/archive\"\n", + "os.path.exists(DATA_DIR)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "586993fd", + "metadata": {}, + "outputs": [], + "source": [ + "# Speicify datastream_dir following the path conventions and check its existence\n", + "DATASTREAM_NAME = \"mascparticles\"\n", + "DATA_LEVEL = \"c1\"\n", + "site = \"oli\"\n", + "facility = \"M1\"\n", + "datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )\n", + "print(datastream_dir)\n", + "print(os.path.exists(datastream_dir))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0742f7c1", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: list 5 (random) files under datastream_dir\n", + "files = os.listdir(datastream_dir)\n", + "files[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39b98a36", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: get most recent file\n", + "list_of_files = glob.glob(f\"{datastream_dir}/*\") # * means all if need specific format then *.csv\n", + "latest_file = max(list_of_files, key=os.path.getctime)\n", + "latest_file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "902d514e", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus sort datastream files based on datetime\n", + "files = os.listdir(datastream_dir)\n", + "file_sorted = files.copy()\n", + "file_sorted.sort() \n", + "print(file_sorted[:5])\n", + "\n", + "# to reverse\n", + "file_sorted_reverse = files.copy() \n", + "file_sorted_reverse.sort(reverse=True)\n", + "print(file_sorted_reverse[:5])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec5923b2", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: pattern matching\n", + "# filter the 200709** files under datastream_dir\n", + "files_filter = glob.glob(f'{datastream_dir}/*.200709*.*')\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c4a4aa18", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: use armnotebook_utils.file_filter (TODO) to filter files based on datastream info \n" + ] + }, + { + "cell_type": "markdown", + "id": "cfeb9efc", + "metadata": {}, + "source": [ + "### Load data\n", + "The arm data is stored in [NetCDF](https://en.wikipedia.org/wiki/NetCDF#:~:text=%22NetCDF%20(network%20Common%20Data%20Form,format%20for%20representing%20scientific%20data.) format. We can use xarray's `open_dataset` method to load single file, or `open_mfdataset` to open multiple files. (Note: the latter will still return a single xarray dataset object by combining multiple files.)\n", + "\n", + "Note: open_dataset keeps the file handle open and lazy loads its contents. All parameters are passed directly to open_dataset. It is a preferable method over load_dataset for memory-effiency. (more details, please see [xarray.open_dataset](https://docs.xarray.dev/en/stable/generated/xarray.open_dataset.html))\n", + "\n", + "See the following example in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a440a329", + "metadata": {}, + "outputs": [], + "source": [ + "# open single file\n", + "full_path = latest_file\n", + "print(full_path)\n", + "ds_single = xr.open_dataset(full_path)\n", + "ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0143a3d", + "metadata": {}, + "outputs": [], + "source": [ + "print(ds_single)\n", + "print(type(ds_single))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c0f8939", + "metadata": {}, + "outputs": [], + "source": [ + "# open multiple files\n", + "n_files = 3\n", + "full_paths = [os.path.join(datastream_dir, f_path) for f_path in file_sorted_reverse[:n_files]]\n", + "print(full_paths)\n", + "try: # Note: sometimes multiple files cannot be merged, so we used try except here.\n", + " ds_mutiple = xr.open_mfdataset(full_paths)\n", + " # ds_mutiple\n", + " print(type(ds_mutiple))\n", + " print(ds_mutiple)\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "3fa59943", + "metadata": {}, + "source": [ + "## Explore the data" + ] + }, + { + "cell_type": "markdown", + "id": "571f69b9", + "metadata": {}, + "source": [ + "### NetCDF Data structure \n", + "\n", + "\n", + "(If you are confident with NetCDF basics and xarray essentials, feel free to skip this session.)\n", + "Before we dive into data exploration, there are some eseential concpets we should be familiar with (shown below): \n", + "* Dataset\n", + "* Data array\n", + "* Variable\n", + "* Dimenssion\n", + "* Coordinate\n", + "* Data Type\n", + "* Meta Data (Attributes)\n", + "\n", + "We will not go into details about NetCDF basics and here are some references you might find helpful\n", + "* [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#)\n", + "* [Network Common Data Form (NetCDF)](https://www.unidata.ucar.edu/software/netcdf/) and [A Brief History of (netCDF) Time](https://www.unidata.ucar.edu/software/netcdf/time/recs.html)\n", + "\n", + "\n", + "\n", + "### Xarray essentials\n", + "Earlier we introduced the xarray pacakge and used `open_dataset` and `open_mfdataset` to retrieve NetCDF data file as an xarray Dataset object. Recall that you can review the data in a notebook by using `print(ds)` or `ds`. \n", + "\n", + "Using xarray to retrieve the aforementioned NetCDF basics is straightforwared. In general,\n", + "* Dataset: `ds`\n", + "* Data array: `ds.variables`\n", + "* Variable: `ds.ds.variables`\n", + "* Dimenssion: `ds.dims`\n", + "* Coordinate: `ds.coords`\n", + "* Data Type: `type`\n", + "* Meta Data (Attributes)\n", + "\n", + "Also, here are some references if you are new to xarray\n", + "* [Xarray in 45 minutes](https://tutorial.xarray.dev/overview/xarray-in-45-min.html)\n", + "* [Handling NetCDF Files using XArray for Absolute Beginners](https://towardsdatascience.com/handling-netcdf-files-using-xarray-for-absolute-beginners-111a8ab4463f)\n", + "\n", + "Try the following commands in action\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b3eac323", + "metadata": {}, + "outputs": [], + "source": [ + "ds = ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77ecf85d", + "metadata": {}, + "outputs": [], + "source": [ + "# Dataset \n", + "ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25e7de09", + "metadata": {}, + "outputs": [], + "source": [ + "# Data array, Variable\n", + "# Note: the info can be overwhelming. Do not attempt to grasp everything at the first glance.\n", + "ds.variables" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c41b67e", + "metadata": {}, + "outputs": [], + "source": [ + "# dimenssions\n", + "ds.dims" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "156f1dfc", + "metadata": {}, + "outputs": [], + "source": [ + "# coordinates\n", + "ds.coords" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "277d6064", + "metadata": {}, + "outputs": [], + "source": [ + "# Meta Data (Attributes)\n", + "ds.attrs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d334681f", + "metadata": {}, + "outputs": [], + "source": [ + "# type\n", + "print(type(ds))\n", + "print(type(ds.variables))\n", + "print(type(ds.dims))\n", + "print(type(ds.coords))\n", + "print(type(ds.attrs))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "643399d6", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: retrieve variable names only\n", + "list(ds.variables)" + ] + }, + { + "cell_type": "markdown", + "id": "1cf36878", + "metadata": {}, + "source": [ + "#### Discussion: variable vs. coordinates vs. dimenssions. \n", + "From [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#):\n", + "\n", + "* Dimession: A dimension may be used to represent a real physical dimension, for example, time, latitude, longitude, or height. A dimension might also be used to index other quantities, for example station or model-run-number.\n", + "A netCDF dimension has both a name and a length. A dimension length is an arbitrary positive integer, except that one dimension in a netCDF dataset can have the length UNLIMITED.\n", + "\n", + "* Variables: Variables are used to store the bulk of the data in a netCDF dataset. A variable represents an array of values of the same type. A scalar value is treated as a 0-dimensional array. A variable has a name, a data type, and a shape described by its list of dimensions specified when the variable is created. A variable may also have associated attributes, which may be added, deleted or changed after the variable is created.\n", + "\n", + "* Coordinate (Variables): It is legal for a variable to have the same name as a dimension. Such variables have no special meaning to the netCDF library. However there is a convention that such variables should be treated in a special way by software using this library.\n", + "A variable with the same name as a dimension is called a coordinate variable. It typically defines a physical coordinate corresponding to that dimension.\n", + "\n", + "\n", + "Tips: By definition, coordinates and dimenssions are also variable. For example ds.time is a coordinate but it is also a special variable. But in practice, when we talk about variable, it implies regular/non-coordinate variable. We will use this convention for the remaining notebook. " + ] + }, + { + "cell_type": "markdown", + "id": "d47120f9", + "metadata": {}, + "source": [ + "### Xarray Variable\n", + "\n", + "\n", + "We can use `ds.variables` to access varialbes of a dataset. But many times viewing print-out the whole datasets can be overwhemling. Instead, we would work on the individual variable (also called Xarray data array.) using the `ds[var_name]` syntax, where `var_name` is the name of the variable. See the following examples in action.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5a57e136", + "metadata": {}, + "outputs": [], + "source": [ + "print(type(ds[\"time\"]))\n", + "ds[\"time\"]" + ] + }, + { + "cell_type": "markdown", + "id": "51ec643e", + "metadata": {}, + "source": [ + "#### Variable properties\n", + "For individual varible, the following properties are mostly used with the assocaited xarray method to retrieve them. (assuming var = ds[var_name])\n", + "* name: `var.name`\n", + "* data content: `var.data`\n", + "* attributes: `var.attrs`\n", + "* dimenstions: `var.dims`\n", + "* data type: `var.data.dtype`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a632a525", + "metadata": {}, + "outputs": [], + "source": [ + "var_name = \"time\"\n", + "var = ds[var_name]\n", + "\n", + "print(\"var.name: \\n\", var.name, \"\\n\")\n", + "print(\"var.data: \\n\", var.data, \"\\n\")\n", + "print(\"var.attrs: \\n\", var.attrs, \"\\n\")\n", + "print(\"var.dims: \\n\", var.dims, \"\\n\")\n", + "print(\"var.data.dtype: \\n\", var.data.dtype, \"\\n\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b256b07f", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: indivdual variable info table (collect and display variable info as a tabular format)\n", + "df_info = pd.DataFrame()\n", + "df_info[\"var_name\"] = list(ds.variables)\n", + "df_info[\"dims\"] = df_info.var_name.apply(lambda x: ds[x].dims)\n", + "df_info[\"is_dim\"] = df_info.var_name.apply(lambda x: x in ds.dims)\n", + "df_info[\"n_dim\"] = df_info.var_name.apply(lambda x: len(ds[x].dims))\n", + "df_info[\"attrs\"] = df_info.var_name.apply(lambda x: ds[x].attrs)\n", + "df_info[\"dtype\"] = df_info.var_name.apply(lambda x: ds[x].data.dtype)\n", + "df_info" + ] + }, + { + "cell_type": "markdown", + "id": "e6a36b96", + "metadata": {}, + "source": [ + "### Data cleaning/Preprocessing (skipped)\n", + "Data cleaning and preprocessing is an important stage in the the data analysis pipeline. However it will be out of the scope of this notebook. For more information about data cleaning and preprocessing basics, here are some references. \n", + "* [Xarray Fundamentals](https://earth-env-data-science.github.io/lectures/xarray/xarray.html)\n", + "* [Xarray Tutorial — Pangeo Gallery documentation](http://gallery.pangeo.io/repos/pangeo-data/pangeo-tutorial-gallery/xarray.html)\n", + "* [Pythonic Data Cleaning With pandas and NumPy](https://realpython.com/python-data-cleaning-numpy-pandas/)\n", + "* [Pandas - Cleaning Data](https://www.w3schools.com/python/pandas/pandas_cleaning.asp)" + ] + }, + { + "cell_type": "markdown", + "id": "ae6e1a69", + "metadata": {}, + "source": [ + "### Plotting\n", + "Data visualization (or plotting) is another important data analysis topic and deserves its own discussion. Here in this notebook we will only demonstrate basic tool for simple data visualization tasks.\n", + "\n", + "Here are some reference you might find useful:\n", + "* xarray plotting: https://docs.xarray.dev/en/stable/user-guide/plotting.html\n", + "* Atmospheric Community Toolkit (ACT): https://arm-doe.github.io/ACT/index.html\n", + "\n", + "Note: this notebook is auto-generated using a template. It uses a general idea to select variable(s) to plot and is not customized to each indivdual notebook. Feel free to change the variables in intrrests, especially certain figure is failed to plot." + ] + }, + { + "cell_type": "markdown", + "id": "7e417fb2", + "metadata": {}, + "source": [ + "#### 1-dimenssional basic time series plot\n", + "\n", + "For the following plot we would like to find variables such that\n", + "* it has one and only one dimession\n", + "* \"time\" is its coordinate variable\n", + "* it is not a dimenssion itself,\n", + "* it is not a special variable with substrings within [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "\n", + "Please see the following example in action. (For more details about pandas filtering, please see the following references.)\n", + "* [pandas: multiple conditions while indexing data frame](https://stackoverflow.com/questions/22591174/pandas-multiple-conditions-while-indexing-data-frame-unexpected-behavior)\n", + "* [pandas.DataFrame.apply](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.apply.html)\n", + "* [How to test if a string contains one of the substrings in a list...](https://stackoverflow.com/questions/26577516/how-to-test-if-a-string-contains-one-of-the-substrings-in-a-list-in-pandas" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30edac2d", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==1, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter = df_info[(df_info.n_dim == 1) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4777a659", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_1d = df_filter.var_name.values[0]\n", + " var_1d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb5ae985", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " ds[var_1d].plot()\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "9e41066e", + "metadata": {}, + "source": [ + "#### 2-dimenssional basic plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09bd4adc", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==2, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_2 = df_info[(df_info.n_dim == 2) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2cdea1a9", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_2d = df_filter_2.var_name.values[0]\n", + " var_2d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5f48879", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " print(ds[var_2d].dims)\n", + " # ds[var_2d].plot()\n", + "\n", + " # conventionally, use \"time\" as x-axis\n", + " ds[var_2d].plot(x=\"time\")\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "7554b2a6", + "metadata": {}, + "source": [ + "#### qc-plotting (optional)\n", + "\n", + "Note: act qc plotting has more strict requirement, one of them is the associated qc variable need to have \"flag_masks\" attributes. Which is added by using ds = act.io.armfiles.read_netcdf(files_list), then ds.clean.cleanup()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71c2096f", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " ds_act = act.io.armfiles.read_netcdf(full_path)\n", + " print(type(ds_act))\n", + " ds_act.clean.cleanup()\n", + "\n", + " # or \n", + " # ds.clean.cleanup()\n", + "except Exception as e:\n", + " print(\"ERROR\", e)\n", + " ds_act = ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "844f8505", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter valid variables for ACT qc plotting\n", + "condition = (\"qc_\" + df_info.var_name).apply(lambda x: ds_act[x].attrs.get(\"flag_masks\") is not None \n", + " if x in list(ds.variables) else False)\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_3 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings))) &\n", + " condition\n", + " ]\n", + "df_filter_3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd38cc6", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "try:\n", + " qc_variable = df_filter_3.var_name.values[0]\n", + " print(qc_variable)\n", + "except Exception as e:\n", + " print(e)\n", + " \n", + "try:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + " qc_ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "d76e4d27", + "metadata": {}, + "source": [ + "#### bonus: choose variables to plot from a dropdown menu " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb733804", + "metadata": {}, + "outputs": [], + "source": [ + "# Valid variables filtering\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_4 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82d5c20d", + "metadata": {}, + "outputs": [], + "source": [ + "# example 1: using xarray plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2fc9aaa4", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# fig, ax = plt.subplots(figsize=(10, 4))\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "# fig.clear() # Remove old lines from plot and plot new one\n", + "# if len(ds[var].dims)==2:\n", + "# ds[var].plot(x=\"time\", add_colorbar=False)\n", + "# else:\n", + "# ds[var].plot()\n", + "# plt.grid()\n", + "# plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fabdd802", + "metadata": {}, + "outputs": [], + "source": [ + "# example 2: using act plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "962b4186", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "\n", + "\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "\n", + "# i_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + "# i_display.add_subplots((1,), figsize=(10, 4))\n", + "# ax = i_display.plot(var, subplot_index=(0,), set_title=f\"{var} ({ds_act[var].attrs['long_name']})\",)\n", + "\n", + "# ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "# ax.grid()\n", + "# plt.show()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/MASCPARTICLES/mascparticles.c1.ipynb b/VAPs/quicklook/MASCPARTICLES/mascparticles.c1.ipynb new file mode 100644 index 00000000..0c674843 --- /dev/null +++ b/VAPs/quicklook/MASCPARTICLES/mascparticles.c1.ipynb @@ -0,0 +1,339 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# MASCPARTICLES.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/mascparticles) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'mascparticles'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2018-07-16', 'facility': 'M1', 'site': 'oli', 'start_date': '2015-11-01'}, {'end_date': '2023-12-18', 'facility': 'C1', 'site': 'nsa', 'start_date': '2021-04-27'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'oli', 'M1' )\n", + "\n", + "date_start = '2018-07-14'\n", + "date_end = '2018-07-16'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['snowflake_fall_speed', 'maximum_dimension_avg']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'snowflake_fall_speed'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'snowflake_fall_speed'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/MASCPARTICLES/mascparticlesavg.c1.ipynb b/VAPs/quicklook/MASCPARTICLES/mascparticlesavg.c1.ipynb new file mode 100644 index 00000000..4af2c6c2 --- /dev/null +++ b/VAPs/quicklook/MASCPARTICLES/mascparticlesavg.c1.ipynb @@ -0,0 +1,339 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# MASCPARTICLESAVG.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/mascparticles) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'mascparticlesavg'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2018-07-18', 'facility': 'M1', 'site': 'oli', 'start_date': '2015-11-04'}, {'end_date': '2023-12-18', 'facility': 'C1', 'site': 'nsa', 'start_date': '2021-05-06'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'oli', 'M1' )\n", + "\n", + "date_start = '2018-07-16'\n", + "date_end = '2018-07-18'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['fall_speed_avg', 'maximum_dimension_avg']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'num_particles_total'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'fall_speed_avg'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/MERGED-COMMON/MERGED-COMMON_tutorial.ipynb b/VAPs/quicklook/MERGED-COMMON/MERGED-COMMON_tutorial.ipynb new file mode 100644 index 00000000..085cc66b --- /dev/null +++ b/VAPs/quicklook/MERGED-COMMON/MERGED-COMMON_tutorial.ipynb @@ -0,0 +1,838 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# AAFMERGEDCLDSD.C1 Notebook\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/merged-common) for more information about this vap." + ] + }, + { + "cell_type": "markdown", + "id": "97097763", + "metadata": {}, + "source": [ + "In this notebook, we demonstrate the workflow to explore ARM vap data (using aafmergedcldsd as an example.) Value-added products (VAPs) are higher-order data products that have been analyzed and processed to ease scientists’ use of ARM data in atmospheric research and global climate models. \n", + "Here is the main content we will cover." + ] + }, + { + "cell_type": "markdown", + "id": "eddec40f", + "metadata": {}, + "source": [ + "# Table of Content\n", + "## Access the data\n", + "* How to retrieve the data\n", + "* Data path and file name conventions\n", + "* Load data\n", + "## Explore the data\n", + "* NetCDF Data structure\n", + "* Xarray essentials\n", + "* Xarray Variable\n", + "## Plot the data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "import random\n", + "\n", + "import glob\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "2372d5be", + "metadata": {}, + "source": [ + "## Access the data" + ] + }, + { + "cell_type": "markdown", + "id": "5b0d9684", + "metadata": {}, + "source": [ + "### How to retrieve the data\n", + "We assume the path \"/data/archive\" is available where you are running this notebook. You can use `os.path.exists(\"/data/archive\")` to verify if the path exists at your machine.\n", + "\n", + "\n", + "### Data path and file name conventions\n", + "There several common terminologies regarding ARM data, for example, data-stream-name, data-level, site, facility, instrument, etc. (For more details, please see [ARM Data File Standards Version 1.2](https://www.google.com/search?q=arm+datastream+facility+cite+definition&rlz=1C1GCEJ_enUS1029US1029&ei=hb41ZICrBPukqtsPvMODIA&ved=0ahUKEwjAgeiS0aL-AhV7kmoFHbzhAAQQ4dUDCBA&uact=5&oq=arm+datastream+facility+cite+definition&gs_lcp=Cgxnd3Mtd2l6LXNlcnAQAzIFCAAQogQyBQgAEKIEMgUIABCiBDIFCAAQogQyBQgAEKIEOgoIABBHENYEELADOgoIIRCgARDDBBAKSgQIQRgAUJ4DWIoOYO0PaAFwAXgAgAGMAYgBuQmSAQMyLjmYAQCgAQHIAQjAAQE&sclient=gws-wiz-serp).) \n", + "\n", + "For example, this notebook is called `aafmergedcldsd.c1`, where `aafmergedcldsd` is the \"datastream name\", and the `{process.ds_class_level}}` is the \"data level\".\n", + "\n", + "This datastream also contains site `cor` and facility `F1`. (Note: individual datastream might have multiple site-facility pairs.)\n", + "In such a case, the data of this data-stream is stored at `/data/archive/cor/coraafmergedcldsdF1.c1`, which is in the format of `//.`. We can use the following method to assign the data-stream directory `datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )`\n", + "\n", + "The data files under datastream_dir also follows naming conventions. But once reach the datastream_dir level, the most import file naming convention to differentiate the files is \"yyyyMMdd.hhmmss\", which comes handy to filter out files based on datetime. For example, we can use `glob.glob(f'{datastream_dir}/*.200709*.*')` to filter files in 2007 September.\n", + "\n", + "(Note: refer to the https://adc.arm.gov/discovery/#/ and https://adc.arm.gov/solr8/metadata/select API to explore ARM datastream and assoicated available sites and facilities\n", + "\n", + "Please see the following examples in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d7e9eb85", + "metadata": {}, + "outputs": [], + "source": [ + "# Verify if DATA_DIR path exists\n", + "DATA_DIR = \"/data/archive\"\n", + "os.path.exists(DATA_DIR)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "586993fd", + "metadata": {}, + "outputs": [], + "source": [ + "# Speicify datastream_dir following the path conventions and check its existence\n", + "DATASTREAM_NAME = \"aafmergedcldsd\"\n", + "DATA_LEVEL = \"c1\"\n", + "site = \"cor\"\n", + "facility = \"F1\"\n", + "datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )\n", + "print(datastream_dir)\n", + "print(os.path.exists(datastream_dir))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0742f7c1", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: list 5 (random) files under datastream_dir\n", + "files = os.listdir(datastream_dir)\n", + "files[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39b98a36", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: get most recent file\n", + "list_of_files = glob.glob(f\"{datastream_dir}/*\") # * means all if need specific format then *.csv\n", + "latest_file = max(list_of_files, key=os.path.getctime)\n", + "latest_file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "902d514e", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus sort datastream files based on datetime\n", + "files = os.listdir(datastream_dir)\n", + "file_sorted = files.copy()\n", + "file_sorted.sort() \n", + "print(file_sorted[:5])\n", + "\n", + "# to reverse\n", + "file_sorted_reverse = files.copy() \n", + "file_sorted_reverse.sort(reverse=True)\n", + "print(file_sorted_reverse[:5])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec5923b2", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: pattern matching\n", + "# filter the 200709** files under datastream_dir\n", + "files_filter = glob.glob(f'{datastream_dir}/*.200709*.*')\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c4a4aa18", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: use armnotebook_utils.file_filter (TODO) to filter files based on datastream info \n" + ] + }, + { + "cell_type": "markdown", + "id": "cfeb9efc", + "metadata": {}, + "source": [ + "### Load data\n", + "The arm data is stored in [NetCDF](https://en.wikipedia.org/wiki/NetCDF#:~:text=%22NetCDF%20(network%20Common%20Data%20Form,format%20for%20representing%20scientific%20data.) format. We can use xarray's `open_dataset` method to load single file, or `open_mfdataset` to open multiple files. (Note: the latter will still return a single xarray dataset object by combining multiple files.)\n", + "\n", + "Note: open_dataset keeps the file handle open and lazy loads its contents. All parameters are passed directly to open_dataset. It is a preferable method over load_dataset for memory-effiency. (more details, please see [xarray.open_dataset](https://docs.xarray.dev/en/stable/generated/xarray.open_dataset.html))\n", + "\n", + "See the following example in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a440a329", + "metadata": {}, + "outputs": [], + "source": [ + "# open single file\n", + "full_path = latest_file\n", + "print(full_path)\n", + "ds_single = xr.open_dataset(full_path)\n", + "ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0143a3d", + "metadata": {}, + "outputs": [], + "source": [ + "print(ds_single)\n", + "print(type(ds_single))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c0f8939", + "metadata": {}, + "outputs": [], + "source": [ + "# open multiple files\n", + "n_files = 3\n", + "full_paths = [os.path.join(datastream_dir, f_path) for f_path in file_sorted_reverse[:n_files]]\n", + "print(full_paths)\n", + "try: # Note: sometimes multiple files cannot be merged, so we used try except here.\n", + " ds_mutiple = xr.open_mfdataset(full_paths)\n", + " # ds_mutiple\n", + " print(type(ds_mutiple))\n", + " print(ds_mutiple)\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "3fa59943", + "metadata": {}, + "source": [ + "## Explore the data" + ] + }, + { + "cell_type": "markdown", + "id": "571f69b9", + "metadata": {}, + "source": [ + "### NetCDF Data structure \n", + "\n", + "\n", + "(If you are confident with NetCDF basics and xarray essentials, feel free to skip this session.)\n", + "Before we dive into data exploration, there are some eseential concpets we should be familiar with (shown below): \n", + "* Dataset\n", + "* Data array\n", + "* Variable\n", + "* Dimenssion\n", + "* Coordinate\n", + "* Data Type\n", + "* Meta Data (Attributes)\n", + "\n", + "We will not go into details about NetCDF basics and here are some references you might find helpful\n", + "* [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#)\n", + "* [Network Common Data Form (NetCDF)](https://www.unidata.ucar.edu/software/netcdf/) and [A Brief History of (netCDF) Time](https://www.unidata.ucar.edu/software/netcdf/time/recs.html)\n", + "\n", + "\n", + "\n", + "### Xarray essentials\n", + "Earlier we introduced the xarray pacakge and used `open_dataset` and `open_mfdataset` to retrieve NetCDF data file as an xarray Dataset object. Recall that you can review the data in a notebook by using `print(ds)` or `ds`. \n", + "\n", + "Using xarray to retrieve the aforementioned NetCDF basics is straightforwared. In general,\n", + "* Dataset: `ds`\n", + "* Data array: `ds.variables`\n", + "* Variable: `ds.ds.variables`\n", + "* Dimenssion: `ds.dims`\n", + "* Coordinate: `ds.coords`\n", + "* Data Type: `type`\n", + "* Meta Data (Attributes)\n", + "\n", + "Also, here are some references if you are new to xarray\n", + "* [Xarray in 45 minutes](https://tutorial.xarray.dev/overview/xarray-in-45-min.html)\n", + "* [Handling NetCDF Files using XArray for Absolute Beginners](https://towardsdatascience.com/handling-netcdf-files-using-xarray-for-absolute-beginners-111a8ab4463f)\n", + "\n", + "Try the following commands in action\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b3eac323", + "metadata": {}, + "outputs": [], + "source": [ + "ds = ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77ecf85d", + "metadata": {}, + "outputs": [], + "source": [ + "# Dataset \n", + "ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25e7de09", + "metadata": {}, + "outputs": [], + "source": [ + "# Data array, Variable\n", + "# Note: the info can be overwhelming. Do not attempt to grasp everything at the first glance.\n", + "ds.variables" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c41b67e", + "metadata": {}, + "outputs": [], + "source": [ + "# dimenssions\n", + "ds.dims" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "156f1dfc", + "metadata": {}, + "outputs": [], + "source": [ + "# coordinates\n", + "ds.coords" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "277d6064", + "metadata": {}, + "outputs": [], + "source": [ + "# Meta Data (Attributes)\n", + "ds.attrs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d334681f", + "metadata": {}, + "outputs": [], + "source": [ + "# type\n", + "print(type(ds))\n", + "print(type(ds.variables))\n", + "print(type(ds.dims))\n", + "print(type(ds.coords))\n", + "print(type(ds.attrs))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "643399d6", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: retrieve variable names only\n", + "list(ds.variables)" + ] + }, + { + "cell_type": "markdown", + "id": "1cf36878", + "metadata": {}, + "source": [ + "#### Discussion: variable vs. coordinates vs. dimenssions. \n", + "From [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#):\n", + "\n", + "* Dimession: A dimension may be used to represent a real physical dimension, for example, time, latitude, longitude, or height. A dimension might also be used to index other quantities, for example station or model-run-number.\n", + "A netCDF dimension has both a name and a length. A dimension length is an arbitrary positive integer, except that one dimension in a netCDF dataset can have the length UNLIMITED.\n", + "\n", + "* Variables: Variables are used to store the bulk of the data in a netCDF dataset. A variable represents an array of values of the same type. A scalar value is treated as a 0-dimensional array. A variable has a name, a data type, and a shape described by its list of dimensions specified when the variable is created. A variable may also have associated attributes, which may be added, deleted or changed after the variable is created.\n", + "\n", + "* Coordinate (Variables): It is legal for a variable to have the same name as a dimension. Such variables have no special meaning to the netCDF library. However there is a convention that such variables should be treated in a special way by software using this library.\n", + "A variable with the same name as a dimension is called a coordinate variable. It typically defines a physical coordinate corresponding to that dimension.\n", + "\n", + "\n", + "Tips: By definition, coordinates and dimenssions are also variable. For example ds.time is a coordinate but it is also a special variable. But in practice, when we talk about variable, it implies regular/non-coordinate variable. We will use this convention for the remaining notebook. " + ] + }, + { + "cell_type": "markdown", + "id": "d47120f9", + "metadata": {}, + "source": [ + "### Xarray Variable\n", + "\n", + "\n", + "We can use `ds.variables` to access varialbes of a dataset. But many times viewing print-out the whole datasets can be overwhemling. Instead, we would work on the individual variable (also called Xarray data array.) using the `ds[var_name]` syntax, where `var_name` is the name of the variable. See the following examples in action.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5a57e136", + "metadata": {}, + "outputs": [], + "source": [ + "print(type(ds[\"time\"]))\n", + "ds[\"time\"]" + ] + }, + { + "cell_type": "markdown", + "id": "51ec643e", + "metadata": {}, + "source": [ + "#### Variable properties\n", + "For individual varible, the following properties are mostly used with the assocaited xarray method to retrieve them. (assuming var = ds[var_name])\n", + "* name: `var.name`\n", + "* data content: `var.data`\n", + "* attributes: `var.attrs`\n", + "* dimenstions: `var.dims`\n", + "* data type: `var.data.dtype`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a632a525", + "metadata": {}, + "outputs": [], + "source": [ + "var_name = \"time\"\n", + "var = ds[var_name]\n", + "\n", + "print(\"var.name: \\n\", var.name, \"\\n\")\n", + "print(\"var.data: \\n\", var.data, \"\\n\")\n", + "print(\"var.attrs: \\n\", var.attrs, \"\\n\")\n", + "print(\"var.dims: \\n\", var.dims, \"\\n\")\n", + "print(\"var.data.dtype: \\n\", var.data.dtype, \"\\n\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b256b07f", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: indivdual variable info table (collect and display variable info as a tabular format)\n", + "df_info = pd.DataFrame()\n", + "df_info[\"var_name\"] = list(ds.variables)\n", + "df_info[\"dims\"] = df_info.var_name.apply(lambda x: ds[x].dims)\n", + "df_info[\"is_dim\"] = df_info.var_name.apply(lambda x: x in ds.dims)\n", + "df_info[\"n_dim\"] = df_info.var_name.apply(lambda x: len(ds[x].dims))\n", + "df_info[\"attrs\"] = df_info.var_name.apply(lambda x: ds[x].attrs)\n", + "df_info[\"dtype\"] = df_info.var_name.apply(lambda x: ds[x].data.dtype)\n", + "df_info" + ] + }, + { + "cell_type": "markdown", + "id": "e6a36b96", + "metadata": {}, + "source": [ + "### Data cleaning/Preprocessing (skipped)\n", + "Data cleaning and preprocessing is an important stage in the the data analysis pipeline. However it will be out of the scope of this notebook. For more information about data cleaning and preprocessing basics, here are some references. \n", + "* [Xarray Fundamentals](https://earth-env-data-science.github.io/lectures/xarray/xarray.html)\n", + "* [Xarray Tutorial — Pangeo Gallery documentation](http://gallery.pangeo.io/repos/pangeo-data/pangeo-tutorial-gallery/xarray.html)\n", + "* [Pythonic Data Cleaning With pandas and NumPy](https://realpython.com/python-data-cleaning-numpy-pandas/)\n", + "* [Pandas - Cleaning Data](https://www.w3schools.com/python/pandas/pandas_cleaning.asp)" + ] + }, + { + "cell_type": "markdown", + "id": "ae6e1a69", + "metadata": {}, + "source": [ + "### Plotting\n", + "Data visualization (or plotting) is another important data analysis topic and deserves its own discussion. Here in this notebook we will only demonstrate basic tool for simple data visualization tasks.\n", + "\n", + "Here are some reference you might find useful:\n", + "* xarray plotting: https://docs.xarray.dev/en/stable/user-guide/plotting.html\n", + "* Atmospheric Community Toolkit (ACT): https://arm-doe.github.io/ACT/index.html\n", + "\n", + "Note: this notebook is auto-generated using a template. It uses a general idea to select variable(s) to plot and is not customized to each indivdual notebook. Feel free to change the variables in intrrests, especially certain figure is failed to plot." + ] + }, + { + "cell_type": "markdown", + "id": "7e417fb2", + "metadata": {}, + "source": [ + "#### 1-dimenssional basic time series plot\n", + "\n", + "For the following plot we would like to find variables such that\n", + "* it has one and only one dimession\n", + "* \"time\" is its coordinate variable\n", + "* it is not a dimenssion itself,\n", + "* it is not a special variable with substrings within [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "\n", + "Please see the following example in action. (For more details about pandas filtering, please see the following references.)\n", + "* [pandas: multiple conditions while indexing data frame](https://stackoverflow.com/questions/22591174/pandas-multiple-conditions-while-indexing-data-frame-unexpected-behavior)\n", + "* [pandas.DataFrame.apply](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.apply.html)\n", + "* [How to test if a string contains one of the substrings in a list...](https://stackoverflow.com/questions/26577516/how-to-test-if-a-string-contains-one-of-the-substrings-in-a-list-in-pandas" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30edac2d", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==1, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter = df_info[(df_info.n_dim == 1) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4777a659", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_1d = df_filter.var_name.values[0]\n", + " var_1d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb5ae985", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " ds[var_1d].plot()\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "9e41066e", + "metadata": {}, + "source": [ + "#### 2-dimenssional basic plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09bd4adc", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==2, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_2 = df_info[(df_info.n_dim == 2) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2cdea1a9", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_2d = df_filter_2.var_name.values[0]\n", + " var_2d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5f48879", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " print(ds[var_2d].dims)\n", + " # ds[var_2d].plot()\n", + "\n", + " # conventionally, use \"time\" as x-axis\n", + " ds[var_2d].plot(x=\"time\")\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "7554b2a6", + "metadata": {}, + "source": [ + "#### qc-plotting (optional)\n", + "\n", + "Note: act qc plotting has more strict requirement, one of them is the associated qc variable need to have \"flag_masks\" attributes. Which is added by using ds = act.io.armfiles.read_netcdf(files_list), then ds.clean.cleanup()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71c2096f", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " ds_act = act.io.armfiles.read_netcdf(full_path)\n", + " print(type(ds_act))\n", + " ds_act.clean.cleanup()\n", + "\n", + " # or \n", + " # ds.clean.cleanup()\n", + "except Exception as e:\n", + " print(\"ERROR\", e)\n", + " ds_act = ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "844f8505", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter valid variables for ACT qc plotting\n", + "condition = (\"qc_\" + df_info.var_name).apply(lambda x: ds_act[x].attrs.get(\"flag_masks\") is not None \n", + " if x in list(ds.variables) else False)\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_3 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings))) &\n", + " condition\n", + " ]\n", + "df_filter_3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd38cc6", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "try:\n", + " qc_variable = df_filter_3.var_name.values[0]\n", + " print(qc_variable)\n", + "except Exception as e:\n", + " print(e)\n", + " \n", + "try:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + " qc_ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "d76e4d27", + "metadata": {}, + "source": [ + "#### bonus: choose variables to plot from a dropdown menu " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb733804", + "metadata": {}, + "outputs": [], + "source": [ + "# Valid variables filtering\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_4 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82d5c20d", + "metadata": {}, + "outputs": [], + "source": [ + "# example 1: using xarray plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2fc9aaa4", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# fig, ax = plt.subplots(figsize=(10, 4))\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "# fig.clear() # Remove old lines from plot and plot new one\n", + "# if len(ds[var].dims)==2:\n", + "# ds[var].plot(x=\"time\", add_colorbar=False)\n", + "# else:\n", + "# ds[var].plot()\n", + "# plt.grid()\n", + "# plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fabdd802", + "metadata": {}, + "outputs": [], + "source": [ + "# example 2: using act plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "962b4186", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "\n", + "\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "\n", + "# i_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + "# i_display.add_subplots((1,), figsize=(10, 4))\n", + "# ax = i_display.plot(var, subplot_index=(0,), set_title=f\"{var} ({ds_act[var].attrs['long_name']})\",)\n", + "\n", + "# ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "# ax.grid()\n", + "# plt.show()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/MERGED-COMMON/aafmergedcldsd.c1.ipynb b/VAPs/quicklook/MERGED-COMMON/aafmergedcldsd.c1.ipynb new file mode 100644 index 00000000..09576f45 --- /dev/null +++ b/VAPs/quicklook/MERGED-COMMON/aafmergedcldsd.c1.ipynb @@ -0,0 +1,297 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# AAFMERGEDCLDSD.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/merged-common) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'aafmergedcldsd'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2018-12-08', 'facility': 'F1', 'site': 'cor', 'start_date': '2018-11-04'}, {'end_date': '2018-02-19', 'facility': 'F1', 'site': 'ena', 'start_date': '2017-06-21'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'cor', 'F1' )\n", + "\n", + "date_start = '2018-12-06'\n", + "date_end = '2018-12-08'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['total_number_concentration', 'number_concentration']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'total_number_concentration'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/MERGEDSMPSAPS/MERGEDSMPSAPS_tutorial.ipynb b/VAPs/quicklook/MERGEDSMPSAPS/MERGEDSMPSAPS_tutorial.ipynb new file mode 100644 index 00000000..0b305fe9 --- /dev/null +++ b/VAPs/quicklook/MERGEDSMPSAPS/MERGEDSMPSAPS_tutorial.ipynb @@ -0,0 +1,838 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# MERGEDSMPSAPS.C1 Notebook\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/mergedsmpsaps) for more information about this vap." + ] + }, + { + "cell_type": "markdown", + "id": "97097763", + "metadata": {}, + "source": [ + "In this notebook, we demonstrate the workflow to explore ARM vap data (using mergedsmpsaps as an example.) Value-added products (VAPs) are higher-order data products that have been analyzed and processed to ease scientists’ use of ARM data in atmospheric research and global climate models. \n", + "Here is the main content we will cover." + ] + }, + { + "cell_type": "markdown", + "id": "eddec40f", + "metadata": {}, + "source": [ + "# Table of Content\n", + "## Access the data\n", + "* How to retrieve the data\n", + "* Data path and file name conventions\n", + "* Load data\n", + "## Explore the data\n", + "* NetCDF Data structure\n", + "* Xarray essentials\n", + "* Xarray Variable\n", + "## Plot the data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "import random\n", + "\n", + "import glob\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "2372d5be", + "metadata": {}, + "source": [ + "## Access the data" + ] + }, + { + "cell_type": "markdown", + "id": "5b0d9684", + "metadata": {}, + "source": [ + "### How to retrieve the data\n", + "We assume the path \"/data/archive\" is available where you are running this notebook. You can use `os.path.exists(\"/data/archive\")` to verify if the path exists at your machine.\n", + "\n", + "\n", + "### Data path and file name conventions\n", + "There several common terminologies regarding ARM data, for example, data-stream-name, data-level, site, facility, instrument, etc. (For more details, please see [ARM Data File Standards Version 1.2](https://www.google.com/search?q=arm+datastream+facility+cite+definition&rlz=1C1GCEJ_enUS1029US1029&ei=hb41ZICrBPukqtsPvMODIA&ved=0ahUKEwjAgeiS0aL-AhV7kmoFHbzhAAQQ4dUDCBA&uact=5&oq=arm+datastream+facility+cite+definition&gs_lcp=Cgxnd3Mtd2l6LXNlcnAQAzIFCAAQogQyBQgAEKIEMgUIABCiBDIFCAAQogQyBQgAEKIEOgoIABBHENYEELADOgoIIRCgARDDBBAKSgQIQRgAUJ4DWIoOYO0PaAFwAXgAgAGMAYgBuQmSAQMyLjmYAQCgAQHIAQjAAQE&sclient=gws-wiz-serp).) \n", + "\n", + "For example, this notebook is called `mergedsmpsaps.c1`, where `mergedsmpsaps` is the \"datastream name\", and the `{process.ds_class_level}}` is the \"data level\".\n", + "\n", + "This datastream also contains site `hou` and facility `M1`. (Note: individual datastream might have multiple site-facility pairs.)\n", + "In such a case, the data of this data-stream is stored at `/data/archive/hou/houmergedsmpsapsM1.c1`, which is in the format of `//.`. We can use the following method to assign the data-stream directory `datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )`\n", + "\n", + "The data files under datastream_dir also follows naming conventions. But once reach the datastream_dir level, the most import file naming convention to differentiate the files is \"yyyyMMdd.hhmmss\", which comes handy to filter out files based on datetime. For example, we can use `glob.glob(f'{datastream_dir}/*.200709*.*')` to filter files in 2007 September.\n", + "\n", + "(Note: refer to the https://adc.arm.gov/discovery/#/ and https://adc.arm.gov/solr8/metadata/select API to explore ARM datastream and assoicated available sites and facilities\n", + "\n", + "Please see the following examples in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d7e9eb85", + "metadata": {}, + "outputs": [], + "source": [ + "# Verify if DATA_DIR path exists\n", + "DATA_DIR = \"/data/archive\"\n", + "os.path.exists(DATA_DIR)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "586993fd", + "metadata": {}, + "outputs": [], + "source": [ + "# Speicify datastream_dir following the path conventions and check its existence\n", + "DATASTREAM_NAME = \"mergedsmpsaps\"\n", + "DATA_LEVEL = \"c1\"\n", + "site = \"hou\"\n", + "facility = \"M1\"\n", + "datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )\n", + "print(datastream_dir)\n", + "print(os.path.exists(datastream_dir))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0742f7c1", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: list 5 (random) files under datastream_dir\n", + "files = os.listdir(datastream_dir)\n", + "files[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39b98a36", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: get most recent file\n", + "list_of_files = glob.glob(f\"{datastream_dir}/*\") # * means all if need specific format then *.csv\n", + "latest_file = max(list_of_files, key=os.path.getctime)\n", + "latest_file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "902d514e", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus sort datastream files based on datetime\n", + "files = os.listdir(datastream_dir)\n", + "file_sorted = files.copy()\n", + "file_sorted.sort() \n", + "print(file_sorted[:5])\n", + "\n", + "# to reverse\n", + "file_sorted_reverse = files.copy() \n", + "file_sorted_reverse.sort(reverse=True)\n", + "print(file_sorted_reverse[:5])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec5923b2", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: pattern matching\n", + "# filter the 200709** files under datastream_dir\n", + "files_filter = glob.glob(f'{datastream_dir}/*.200709*.*')\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c4a4aa18", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: use armnotebook_utils.file_filter (TODO) to filter files based on datastream info \n" + ] + }, + { + "cell_type": "markdown", + "id": "cfeb9efc", + "metadata": {}, + "source": [ + "### Load data\n", + "The arm data is stored in [NetCDF](https://en.wikipedia.org/wiki/NetCDF#:~:text=%22NetCDF%20(network%20Common%20Data%20Form,format%20for%20representing%20scientific%20data.) format. We can use xarray's `open_dataset` method to load single file, or `open_mfdataset` to open multiple files. (Note: the latter will still return a single xarray dataset object by combining multiple files.)\n", + "\n", + "Note: open_dataset keeps the file handle open and lazy loads its contents. All parameters are passed directly to open_dataset. It is a preferable method over load_dataset for memory-effiency. (more details, please see [xarray.open_dataset](https://docs.xarray.dev/en/stable/generated/xarray.open_dataset.html))\n", + "\n", + "See the following example in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a440a329", + "metadata": {}, + "outputs": [], + "source": [ + "# open single file\n", + "full_path = latest_file\n", + "print(full_path)\n", + "ds_single = xr.open_dataset(full_path)\n", + "ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0143a3d", + "metadata": {}, + "outputs": [], + "source": [ + "print(ds_single)\n", + "print(type(ds_single))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c0f8939", + "metadata": {}, + "outputs": [], + "source": [ + "# open multiple files\n", + "n_files = 3\n", + "full_paths = [os.path.join(datastream_dir, f_path) for f_path in file_sorted_reverse[:n_files]]\n", + "print(full_paths)\n", + "try: # Note: sometimes multiple files cannot be merged, so we used try except here.\n", + " ds_mutiple = xr.open_mfdataset(full_paths)\n", + " # ds_mutiple\n", + " print(type(ds_mutiple))\n", + " print(ds_mutiple)\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "3fa59943", + "metadata": {}, + "source": [ + "## Explore the data" + ] + }, + { + "cell_type": "markdown", + "id": "571f69b9", + "metadata": {}, + "source": [ + "### NetCDF Data structure \n", + "\n", + "\n", + "(If you are confident with NetCDF basics and xarray essentials, feel free to skip this session.)\n", + "Before we dive into data exploration, there are some eseential concpets we should be familiar with (shown below): \n", + "* Dataset\n", + "* Data array\n", + "* Variable\n", + "* Dimenssion\n", + "* Coordinate\n", + "* Data Type\n", + "* Meta Data (Attributes)\n", + "\n", + "We will not go into details about NetCDF basics and here are some references you might find helpful\n", + "* [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#)\n", + "* [Network Common Data Form (NetCDF)](https://www.unidata.ucar.edu/software/netcdf/) and [A Brief History of (netCDF) Time](https://www.unidata.ucar.edu/software/netcdf/time/recs.html)\n", + "\n", + "\n", + "\n", + "### Xarray essentials\n", + "Earlier we introduced the xarray pacakge and used `open_dataset` and `open_mfdataset` to retrieve NetCDF data file as an xarray Dataset object. Recall that you can review the data in a notebook by using `print(ds)` or `ds`. \n", + "\n", + "Using xarray to retrieve the aforementioned NetCDF basics is straightforwared. In general,\n", + "* Dataset: `ds`\n", + "* Data array: `ds.variables`\n", + "* Variable: `ds.ds.variables`\n", + "* Dimenssion: `ds.dims`\n", + "* Coordinate: `ds.coords`\n", + "* Data Type: `type`\n", + "* Meta Data (Attributes)\n", + "\n", + "Also, here are some references if you are new to xarray\n", + "* [Xarray in 45 minutes](https://tutorial.xarray.dev/overview/xarray-in-45-min.html)\n", + "* [Handling NetCDF Files using XArray for Absolute Beginners](https://towardsdatascience.com/handling-netcdf-files-using-xarray-for-absolute-beginners-111a8ab4463f)\n", + "\n", + "Try the following commands in action\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b3eac323", + "metadata": {}, + "outputs": [], + "source": [ + "ds = ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77ecf85d", + "metadata": {}, + "outputs": [], + "source": [ + "# Dataset \n", + "ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25e7de09", + "metadata": {}, + "outputs": [], + "source": [ + "# Data array, Variable\n", + "# Note: the info can be overwhelming. Do not attempt to grasp everything at the first glance.\n", + "ds.variables" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c41b67e", + "metadata": {}, + "outputs": [], + "source": [ + "# dimenssions\n", + "ds.dims" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "156f1dfc", + "metadata": {}, + "outputs": [], + "source": [ + "# coordinates\n", + "ds.coords" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "277d6064", + "metadata": {}, + "outputs": [], + "source": [ + "# Meta Data (Attributes)\n", + "ds.attrs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d334681f", + "metadata": {}, + "outputs": [], + "source": [ + "# type\n", + "print(type(ds))\n", + "print(type(ds.variables))\n", + "print(type(ds.dims))\n", + "print(type(ds.coords))\n", + "print(type(ds.attrs))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "643399d6", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: retrieve variable names only\n", + "list(ds.variables)" + ] + }, + { + "cell_type": "markdown", + "id": "1cf36878", + "metadata": {}, + "source": [ + "#### Discussion: variable vs. coordinates vs. dimenssions. \n", + "From [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#):\n", + "\n", + "* Dimession: A dimension may be used to represent a real physical dimension, for example, time, latitude, longitude, or height. A dimension might also be used to index other quantities, for example station or model-run-number.\n", + "A netCDF dimension has both a name and a length. A dimension length is an arbitrary positive integer, except that one dimension in a netCDF dataset can have the length UNLIMITED.\n", + "\n", + "* Variables: Variables are used to store the bulk of the data in a netCDF dataset. A variable represents an array of values of the same type. A scalar value is treated as a 0-dimensional array. A variable has a name, a data type, and a shape described by its list of dimensions specified when the variable is created. A variable may also have associated attributes, which may be added, deleted or changed after the variable is created.\n", + "\n", + "* Coordinate (Variables): It is legal for a variable to have the same name as a dimension. Such variables have no special meaning to the netCDF library. However there is a convention that such variables should be treated in a special way by software using this library.\n", + "A variable with the same name as a dimension is called a coordinate variable. It typically defines a physical coordinate corresponding to that dimension.\n", + "\n", + "\n", + "Tips: By definition, coordinates and dimenssions are also variable. For example ds.time is a coordinate but it is also a special variable. But in practice, when we talk about variable, it implies regular/non-coordinate variable. We will use this convention for the remaining notebook. " + ] + }, + { + "cell_type": "markdown", + "id": "d47120f9", + "metadata": {}, + "source": [ + "### Xarray Variable\n", + "\n", + "\n", + "We can use `ds.variables` to access varialbes of a dataset. But many times viewing print-out the whole datasets can be overwhemling. Instead, we would work on the individual variable (also called Xarray data array.) using the `ds[var_name]` syntax, where `var_name` is the name of the variable. See the following examples in action.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5a57e136", + "metadata": {}, + "outputs": [], + "source": [ + "print(type(ds[\"time\"]))\n", + "ds[\"time\"]" + ] + }, + { + "cell_type": "markdown", + "id": "51ec643e", + "metadata": {}, + "source": [ + "#### Variable properties\n", + "For individual varible, the following properties are mostly used with the assocaited xarray method to retrieve them. (assuming var = ds[var_name])\n", + "* name: `var.name`\n", + "* data content: `var.data`\n", + "* attributes: `var.attrs`\n", + "* dimenstions: `var.dims`\n", + "* data type: `var.data.dtype`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a632a525", + "metadata": {}, + "outputs": [], + "source": [ + "var_name = \"time\"\n", + "var = ds[var_name]\n", + "\n", + "print(\"var.name: \\n\", var.name, \"\\n\")\n", + "print(\"var.data: \\n\", var.data, \"\\n\")\n", + "print(\"var.attrs: \\n\", var.attrs, \"\\n\")\n", + "print(\"var.dims: \\n\", var.dims, \"\\n\")\n", + "print(\"var.data.dtype: \\n\", var.data.dtype, \"\\n\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b256b07f", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: indivdual variable info table (collect and display variable info as a tabular format)\n", + "df_info = pd.DataFrame()\n", + "df_info[\"var_name\"] = list(ds.variables)\n", + "df_info[\"dims\"] = df_info.var_name.apply(lambda x: ds[x].dims)\n", + "df_info[\"is_dim\"] = df_info.var_name.apply(lambda x: x in ds.dims)\n", + "df_info[\"n_dim\"] = df_info.var_name.apply(lambda x: len(ds[x].dims))\n", + "df_info[\"attrs\"] = df_info.var_name.apply(lambda x: ds[x].attrs)\n", + "df_info[\"dtype\"] = df_info.var_name.apply(lambda x: ds[x].data.dtype)\n", + "df_info" + ] + }, + { + "cell_type": "markdown", + "id": "e6a36b96", + "metadata": {}, + "source": [ + "### Data cleaning/Preprocessing (skipped)\n", + "Data cleaning and preprocessing is an important stage in the the data analysis pipeline. However it will be out of the scope of this notebook. For more information about data cleaning and preprocessing basics, here are some references. \n", + "* [Xarray Fundamentals](https://earth-env-data-science.github.io/lectures/xarray/xarray.html)\n", + "* [Xarray Tutorial — Pangeo Gallery documentation](http://gallery.pangeo.io/repos/pangeo-data/pangeo-tutorial-gallery/xarray.html)\n", + "* [Pythonic Data Cleaning With pandas and NumPy](https://realpython.com/python-data-cleaning-numpy-pandas/)\n", + "* [Pandas - Cleaning Data](https://www.w3schools.com/python/pandas/pandas_cleaning.asp)" + ] + }, + { + "cell_type": "markdown", + "id": "ae6e1a69", + "metadata": {}, + "source": [ + "### Plotting\n", + "Data visualization (or plotting) is another important data analysis topic and deserves its own discussion. Here in this notebook we will only demonstrate basic tool for simple data visualization tasks.\n", + "\n", + "Here are some reference you might find useful:\n", + "* xarray plotting: https://docs.xarray.dev/en/stable/user-guide/plotting.html\n", + "* Atmospheric Community Toolkit (ACT): https://arm-doe.github.io/ACT/index.html\n", + "\n", + "Note: this notebook is auto-generated using a template. It uses a general idea to select variable(s) to plot and is not customized to each indivdual notebook. Feel free to change the variables in intrrests, especially certain figure is failed to plot." + ] + }, + { + "cell_type": "markdown", + "id": "7e417fb2", + "metadata": {}, + "source": [ + "#### 1-dimenssional basic time series plot\n", + "\n", + "For the following plot we would like to find variables such that\n", + "* it has one and only one dimession\n", + "* \"time\" is its coordinate variable\n", + "* it is not a dimenssion itself,\n", + "* it is not a special variable with substrings within [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "\n", + "Please see the following example in action. (For more details about pandas filtering, please see the following references.)\n", + "* [pandas: multiple conditions while indexing data frame](https://stackoverflow.com/questions/22591174/pandas-multiple-conditions-while-indexing-data-frame-unexpected-behavior)\n", + "* [pandas.DataFrame.apply](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.apply.html)\n", + "* [How to test if a string contains one of the substrings in a list...](https://stackoverflow.com/questions/26577516/how-to-test-if-a-string-contains-one-of-the-substrings-in-a-list-in-pandas" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30edac2d", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==1, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter = df_info[(df_info.n_dim == 1) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4777a659", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_1d = df_filter.var_name.values[0]\n", + " var_1d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb5ae985", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " ds[var_1d].plot()\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "9e41066e", + "metadata": {}, + "source": [ + "#### 2-dimenssional basic plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09bd4adc", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==2, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_2 = df_info[(df_info.n_dim == 2) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2cdea1a9", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_2d = df_filter_2.var_name.values[0]\n", + " var_2d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5f48879", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " print(ds[var_2d].dims)\n", + " # ds[var_2d].plot()\n", + "\n", + " # conventionally, use \"time\" as x-axis\n", + " ds[var_2d].plot(x=\"time\")\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "7554b2a6", + "metadata": {}, + "source": [ + "#### qc-plotting (optional)\n", + "\n", + "Note: act qc plotting has more strict requirement, one of them is the associated qc variable need to have \"flag_masks\" attributes. Which is added by using ds = act.io.armfiles.read_netcdf(files_list), then ds.clean.cleanup()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71c2096f", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " ds_act = act.io.armfiles.read_netcdf(full_path)\n", + " print(type(ds_act))\n", + " ds_act.clean.cleanup()\n", + "\n", + " # or \n", + " # ds.clean.cleanup()\n", + "except Exception as e:\n", + " print(\"ERROR\", e)\n", + " ds_act = ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "844f8505", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter valid variables for ACT qc plotting\n", + "condition = (\"qc_\" + df_info.var_name).apply(lambda x: ds_act[x].attrs.get(\"flag_masks\") is not None \n", + " if x in list(ds.variables) else False)\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_3 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings))) &\n", + " condition\n", + " ]\n", + "df_filter_3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd38cc6", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "try:\n", + " qc_variable = df_filter_3.var_name.values[0]\n", + " print(qc_variable)\n", + "except Exception as e:\n", + " print(e)\n", + " \n", + "try:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + " qc_ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "d76e4d27", + "metadata": {}, + "source": [ + "#### bonus: choose variables to plot from a dropdown menu " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb733804", + "metadata": {}, + "outputs": [], + "source": [ + "# Valid variables filtering\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_4 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82d5c20d", + "metadata": {}, + "outputs": [], + "source": [ + "# example 1: using xarray plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2fc9aaa4", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# fig, ax = plt.subplots(figsize=(10, 4))\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "# fig.clear() # Remove old lines from plot and plot new one\n", + "# if len(ds[var].dims)==2:\n", + "# ds[var].plot(x=\"time\", add_colorbar=False)\n", + "# else:\n", + "# ds[var].plot()\n", + "# plt.grid()\n", + "# plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fabdd802", + "metadata": {}, + "outputs": [], + "source": [ + "# example 2: using act plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "962b4186", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "\n", + "\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "\n", + "# i_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + "# i_display.add_subplots((1,), figsize=(10, 4))\n", + "# ax = i_display.plot(var, subplot_index=(0,), set_title=f\"{var} ({ds_act[var].attrs['long_name']})\",)\n", + "\n", + "# ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "# ax.grid()\n", + "# plt.show()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/MERGEDSMPSAPS/mergedsmpsaps.c1.ipynb b/VAPs/quicklook/MERGEDSMPSAPS/mergedsmpsaps.c1.ipynb new file mode 100644 index 00000000..ff408f74 --- /dev/null +++ b/VAPs/quicklook/MERGEDSMPSAPS/mergedsmpsaps.c1.ipynb @@ -0,0 +1,339 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# MERGEDSMPSAPS.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/mergedsmpsaps) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'mergedsmpsaps'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2022-09-30', 'facility': 'M1', 'site': 'hou', 'start_date': '2021-09-08'}, {'end_date': '2023-11-16', 'facility': 'M1', 'site': 'epc', 'start_date': '2023-01-19'}, {'end_date': '2023-12-09', 'facility': 'E13', 'site': 'sgp', 'start_date': '2016-11-15'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'E13' )\n", + "\n", + "date_start = '2023-12-07'\n", + "date_end = '2023-12-09'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['merged_diameter_mobility', 'merged_dN_dlogDp', 'merged_total_N_conc']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'effective_density'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'merged_diameter_mobility'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/MERGESONDE/MERGESONDE_tutorial.ipynb b/VAPs/quicklook/MERGESONDE/MERGESONDE_tutorial.ipynb new file mode 100644 index 00000000..c2f745f8 --- /dev/null +++ b/VAPs/quicklook/MERGESONDE/MERGESONDE_tutorial.ipynb @@ -0,0 +1,838 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# MERGESONDE1MACE.C1 Notebook\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/mergesonde) for more information about this vap." + ] + }, + { + "cell_type": "markdown", + "id": "97097763", + "metadata": {}, + "source": [ + "In this notebook, we demonstrate the workflow to explore ARM vap data (using mergesonde1mace as an example.) Value-added products (VAPs) are higher-order data products that have been analyzed and processed to ease scientists’ use of ARM data in atmospheric research and global climate models. \n", + "Here is the main content we will cover." + ] + }, + { + "cell_type": "markdown", + "id": "eddec40f", + "metadata": {}, + "source": [ + "# Table of Content\n", + "## Access the data\n", + "* How to retrieve the data\n", + "* Data path and file name conventions\n", + "* Load data\n", + "## Explore the data\n", + "* NetCDF Data structure\n", + "* Xarray essentials\n", + "* Xarray Variable\n", + "## Plot the data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "import random\n", + "\n", + "import glob\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "2372d5be", + "metadata": {}, + "source": [ + "## Access the data" + ] + }, + { + "cell_type": "markdown", + "id": "5b0d9684", + "metadata": {}, + "source": [ + "### How to retrieve the data\n", + "We assume the path \"/data/archive\" is available where you are running this notebook. You can use `os.path.exists(\"/data/archive\")` to verify if the path exists at your machine.\n", + "\n", + "\n", + "### Data path and file name conventions\n", + "There several common terminologies regarding ARM data, for example, data-stream-name, data-level, site, facility, instrument, etc. (For more details, please see [ARM Data File Standards Version 1.2](https://www.google.com/search?q=arm+datastream+facility+cite+definition&rlz=1C1GCEJ_enUS1029US1029&ei=hb41ZICrBPukqtsPvMODIA&ved=0ahUKEwjAgeiS0aL-AhV7kmoFHbzhAAQQ4dUDCBA&uact=5&oq=arm+datastream+facility+cite+definition&gs_lcp=Cgxnd3Mtd2l6LXNlcnAQAzIFCAAQogQyBQgAEKIEMgUIABCiBDIFCAAQogQyBQgAEKIEOgoIABBHENYEELADOgoIIRCgARDDBBAKSgQIQRgAUJ4DWIoOYO0PaAFwAXgAgAGMAYgBuQmSAQMyLjmYAQCgAQHIAQjAAQE&sclient=gws-wiz-serp).) \n", + "\n", + "For example, this notebook is called `mergesonde1mace.c1`, where `mergesonde1mace` is the \"datastream name\", and the `{process.ds_class_level}}` is the \"data level\".\n", + "\n", + "This datastream also contains site `fkb` and facility `M1`. (Note: individual datastream might have multiple site-facility pairs.)\n", + "In such a case, the data of this data-stream is stored at `/data/archive/fkb/fkbmergesonde1maceM1.c1`, which is in the format of `//.`. We can use the following method to assign the data-stream directory `datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )`\n", + "\n", + "The data files under datastream_dir also follows naming conventions. But once reach the datastream_dir level, the most import file naming convention to differentiate the files is \"yyyyMMdd.hhmmss\", which comes handy to filter out files based on datetime. For example, we can use `glob.glob(f'{datastream_dir}/*.200709*.*')` to filter files in 2007 September.\n", + "\n", + "(Note: refer to the https://adc.arm.gov/discovery/#/ and https://adc.arm.gov/solr8/metadata/select API to explore ARM datastream and assoicated available sites and facilities\n", + "\n", + "Please see the following examples in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d7e9eb85", + "metadata": {}, + "outputs": [], + "source": [ + "# Verify if DATA_DIR path exists\n", + "DATA_DIR = \"/data/archive\"\n", + "os.path.exists(DATA_DIR)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "586993fd", + "metadata": {}, + "outputs": [], + "source": [ + "# Speicify datastream_dir following the path conventions and check its existence\n", + "DATASTREAM_NAME = \"mergesonde1mace\"\n", + "DATA_LEVEL = \"c1\"\n", + "site = \"fkb\"\n", + "facility = \"M1\"\n", + "datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )\n", + "print(datastream_dir)\n", + "print(os.path.exists(datastream_dir))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0742f7c1", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: list 5 (random) files under datastream_dir\n", + "files = os.listdir(datastream_dir)\n", + "files[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39b98a36", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: get most recent file\n", + "list_of_files = glob.glob(f\"{datastream_dir}/*\") # * means all if need specific format then *.csv\n", + "latest_file = max(list_of_files, key=os.path.getctime)\n", + "latest_file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "902d514e", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus sort datastream files based on datetime\n", + "files = os.listdir(datastream_dir)\n", + "file_sorted = files.copy()\n", + "file_sorted.sort() \n", + "print(file_sorted[:5])\n", + "\n", + "# to reverse\n", + "file_sorted_reverse = files.copy() \n", + "file_sorted_reverse.sort(reverse=True)\n", + "print(file_sorted_reverse[:5])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec5923b2", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: pattern matching\n", + "# filter the 200709** files under datastream_dir\n", + "files_filter = glob.glob(f'{datastream_dir}/*.200709*.*')\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c4a4aa18", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: use armnotebook_utils.file_filter (TODO) to filter files based on datastream info \n" + ] + }, + { + "cell_type": "markdown", + "id": "cfeb9efc", + "metadata": {}, + "source": [ + "### Load data\n", + "The arm data is stored in [NetCDF](https://en.wikipedia.org/wiki/NetCDF#:~:text=%22NetCDF%20(network%20Common%20Data%20Form,format%20for%20representing%20scientific%20data.) format. We can use xarray's `open_dataset` method to load single file, or `open_mfdataset` to open multiple files. (Note: the latter will still return a single xarray dataset object by combining multiple files.)\n", + "\n", + "Note: open_dataset keeps the file handle open and lazy loads its contents. All parameters are passed directly to open_dataset. It is a preferable method over load_dataset for memory-effiency. (more details, please see [xarray.open_dataset](https://docs.xarray.dev/en/stable/generated/xarray.open_dataset.html))\n", + "\n", + "See the following example in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a440a329", + "metadata": {}, + "outputs": [], + "source": [ + "# open single file\n", + "full_path = latest_file\n", + "print(full_path)\n", + "ds_single = xr.open_dataset(full_path)\n", + "ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0143a3d", + "metadata": {}, + "outputs": [], + "source": [ + "print(ds_single)\n", + "print(type(ds_single))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c0f8939", + "metadata": {}, + "outputs": [], + "source": [ + "# open multiple files\n", + "n_files = 3\n", + "full_paths = [os.path.join(datastream_dir, f_path) for f_path in file_sorted_reverse[:n_files]]\n", + "print(full_paths)\n", + "try: # Note: sometimes multiple files cannot be merged, so we used try except here.\n", + " ds_mutiple = xr.open_mfdataset(full_paths)\n", + " # ds_mutiple\n", + " print(type(ds_mutiple))\n", + " print(ds_mutiple)\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "3fa59943", + "metadata": {}, + "source": [ + "## Explore the data" + ] + }, + { + "cell_type": "markdown", + "id": "571f69b9", + "metadata": {}, + "source": [ + "### NetCDF Data structure \n", + "\n", + "\n", + "(If you are confident with NetCDF basics and xarray essentials, feel free to skip this session.)\n", + "Before we dive into data exploration, there are some eseential concpets we should be familiar with (shown below): \n", + "* Dataset\n", + "* Data array\n", + "* Variable\n", + "* Dimenssion\n", + "* Coordinate\n", + "* Data Type\n", + "* Meta Data (Attributes)\n", + "\n", + "We will not go into details about NetCDF basics and here are some references you might find helpful\n", + "* [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#)\n", + "* [Network Common Data Form (NetCDF)](https://www.unidata.ucar.edu/software/netcdf/) and [A Brief History of (netCDF) Time](https://www.unidata.ucar.edu/software/netcdf/time/recs.html)\n", + "\n", + "\n", + "\n", + "### Xarray essentials\n", + "Earlier we introduced the xarray pacakge and used `open_dataset` and `open_mfdataset` to retrieve NetCDF data file as an xarray Dataset object. Recall that you can review the data in a notebook by using `print(ds)` or `ds`. \n", + "\n", + "Using xarray to retrieve the aforementioned NetCDF basics is straightforwared. In general,\n", + "* Dataset: `ds`\n", + "* Data array: `ds.variables`\n", + "* Variable: `ds.ds.variables`\n", + "* Dimenssion: `ds.dims`\n", + "* Coordinate: `ds.coords`\n", + "* Data Type: `type`\n", + "* Meta Data (Attributes)\n", + "\n", + "Also, here are some references if you are new to xarray\n", + "* [Xarray in 45 minutes](https://tutorial.xarray.dev/overview/xarray-in-45-min.html)\n", + "* [Handling NetCDF Files using XArray for Absolute Beginners](https://towardsdatascience.com/handling-netcdf-files-using-xarray-for-absolute-beginners-111a8ab4463f)\n", + "\n", + "Try the following commands in action\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b3eac323", + "metadata": {}, + "outputs": [], + "source": [ + "ds = ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77ecf85d", + "metadata": {}, + "outputs": [], + "source": [ + "# Dataset \n", + "ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25e7de09", + "metadata": {}, + "outputs": [], + "source": [ + "# Data array, Variable\n", + "# Note: the info can be overwhelming. Do not attempt to grasp everything at the first glance.\n", + "ds.variables" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c41b67e", + "metadata": {}, + "outputs": [], + "source": [ + "# dimenssions\n", + "ds.dims" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "156f1dfc", + "metadata": {}, + "outputs": [], + "source": [ + "# coordinates\n", + "ds.coords" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "277d6064", + "metadata": {}, + "outputs": [], + "source": [ + "# Meta Data (Attributes)\n", + "ds.attrs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d334681f", + "metadata": {}, + "outputs": [], + "source": [ + "# type\n", + "print(type(ds))\n", + "print(type(ds.variables))\n", + "print(type(ds.dims))\n", + "print(type(ds.coords))\n", + "print(type(ds.attrs))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "643399d6", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: retrieve variable names only\n", + "list(ds.variables)" + ] + }, + { + "cell_type": "markdown", + "id": "1cf36878", + "metadata": {}, + "source": [ + "#### Discussion: variable vs. coordinates vs. dimenssions. \n", + "From [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#):\n", + "\n", + "* Dimession: A dimension may be used to represent a real physical dimension, for example, time, latitude, longitude, or height. A dimension might also be used to index other quantities, for example station or model-run-number.\n", + "A netCDF dimension has both a name and a length. A dimension length is an arbitrary positive integer, except that one dimension in a netCDF dataset can have the length UNLIMITED.\n", + "\n", + "* Variables: Variables are used to store the bulk of the data in a netCDF dataset. A variable represents an array of values of the same type. A scalar value is treated as a 0-dimensional array. A variable has a name, a data type, and a shape described by its list of dimensions specified when the variable is created. A variable may also have associated attributes, which may be added, deleted or changed after the variable is created.\n", + "\n", + "* Coordinate (Variables): It is legal for a variable to have the same name as a dimension. Such variables have no special meaning to the netCDF library. However there is a convention that such variables should be treated in a special way by software using this library.\n", + "A variable with the same name as a dimension is called a coordinate variable. It typically defines a physical coordinate corresponding to that dimension.\n", + "\n", + "\n", + "Tips: By definition, coordinates and dimenssions are also variable. For example ds.time is a coordinate but it is also a special variable. But in practice, when we talk about variable, it implies regular/non-coordinate variable. We will use this convention for the remaining notebook. " + ] + }, + { + "cell_type": "markdown", + "id": "d47120f9", + "metadata": {}, + "source": [ + "### Xarray Variable\n", + "\n", + "\n", + "We can use `ds.variables` to access varialbes of a dataset. But many times viewing print-out the whole datasets can be overwhemling. Instead, we would work on the individual variable (also called Xarray data array.) using the `ds[var_name]` syntax, where `var_name` is the name of the variable. See the following examples in action.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5a57e136", + "metadata": {}, + "outputs": [], + "source": [ + "print(type(ds[\"time\"]))\n", + "ds[\"time\"]" + ] + }, + { + "cell_type": "markdown", + "id": "51ec643e", + "metadata": {}, + "source": [ + "#### Variable properties\n", + "For individual varible, the following properties are mostly used with the assocaited xarray method to retrieve them. (assuming var = ds[var_name])\n", + "* name: `var.name`\n", + "* data content: `var.data`\n", + "* attributes: `var.attrs`\n", + "* dimenstions: `var.dims`\n", + "* data type: `var.data.dtype`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a632a525", + "metadata": {}, + "outputs": [], + "source": [ + "var_name = \"time\"\n", + "var = ds[var_name]\n", + "\n", + "print(\"var.name: \\n\", var.name, \"\\n\")\n", + "print(\"var.data: \\n\", var.data, \"\\n\")\n", + "print(\"var.attrs: \\n\", var.attrs, \"\\n\")\n", + "print(\"var.dims: \\n\", var.dims, \"\\n\")\n", + "print(\"var.data.dtype: \\n\", var.data.dtype, \"\\n\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b256b07f", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: indivdual variable info table (collect and display variable info as a tabular format)\n", + "df_info = pd.DataFrame()\n", + "df_info[\"var_name\"] = list(ds.variables)\n", + "df_info[\"dims\"] = df_info.var_name.apply(lambda x: ds[x].dims)\n", + "df_info[\"is_dim\"] = df_info.var_name.apply(lambda x: x in ds.dims)\n", + "df_info[\"n_dim\"] = df_info.var_name.apply(lambda x: len(ds[x].dims))\n", + "df_info[\"attrs\"] = df_info.var_name.apply(lambda x: ds[x].attrs)\n", + "df_info[\"dtype\"] = df_info.var_name.apply(lambda x: ds[x].data.dtype)\n", + "df_info" + ] + }, + { + "cell_type": "markdown", + "id": "e6a36b96", + "metadata": {}, + "source": [ + "### Data cleaning/Preprocessing (skipped)\n", + "Data cleaning and preprocessing is an important stage in the the data analysis pipeline. However it will be out of the scope of this notebook. For more information about data cleaning and preprocessing basics, here are some references. \n", + "* [Xarray Fundamentals](https://earth-env-data-science.github.io/lectures/xarray/xarray.html)\n", + "* [Xarray Tutorial — Pangeo Gallery documentation](http://gallery.pangeo.io/repos/pangeo-data/pangeo-tutorial-gallery/xarray.html)\n", + "* [Pythonic Data Cleaning With pandas and NumPy](https://realpython.com/python-data-cleaning-numpy-pandas/)\n", + "* [Pandas - Cleaning Data](https://www.w3schools.com/python/pandas/pandas_cleaning.asp)" + ] + }, + { + "cell_type": "markdown", + "id": "ae6e1a69", + "metadata": {}, + "source": [ + "### Plotting\n", + "Data visualization (or plotting) is another important data analysis topic and deserves its own discussion. Here in this notebook we will only demonstrate basic tool for simple data visualization tasks.\n", + "\n", + "Here are some reference you might find useful:\n", + "* xarray plotting: https://docs.xarray.dev/en/stable/user-guide/plotting.html\n", + "* Atmospheric Community Toolkit (ACT): https://arm-doe.github.io/ACT/index.html\n", + "\n", + "Note: this notebook is auto-generated using a template. It uses a general idea to select variable(s) to plot and is not customized to each indivdual notebook. Feel free to change the variables in intrrests, especially certain figure is failed to plot." + ] + }, + { + "cell_type": "markdown", + "id": "7e417fb2", + "metadata": {}, + "source": [ + "#### 1-dimenssional basic time series plot\n", + "\n", + "For the following plot we would like to find variables such that\n", + "* it has one and only one dimession\n", + "* \"time\" is its coordinate variable\n", + "* it is not a dimenssion itself,\n", + "* it is not a special variable with substrings within [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "\n", + "Please see the following example in action. (For more details about pandas filtering, please see the following references.)\n", + "* [pandas: multiple conditions while indexing data frame](https://stackoverflow.com/questions/22591174/pandas-multiple-conditions-while-indexing-data-frame-unexpected-behavior)\n", + "* [pandas.DataFrame.apply](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.apply.html)\n", + "* [How to test if a string contains one of the substrings in a list...](https://stackoverflow.com/questions/26577516/how-to-test-if-a-string-contains-one-of-the-substrings-in-a-list-in-pandas" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30edac2d", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==1, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter = df_info[(df_info.n_dim == 1) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4777a659", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_1d = df_filter.var_name.values[0]\n", + " var_1d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb5ae985", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " ds[var_1d].plot()\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "9e41066e", + "metadata": {}, + "source": [ + "#### 2-dimenssional basic plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09bd4adc", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==2, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_2 = df_info[(df_info.n_dim == 2) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2cdea1a9", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_2d = df_filter_2.var_name.values[0]\n", + " var_2d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5f48879", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " print(ds[var_2d].dims)\n", + " # ds[var_2d].plot()\n", + "\n", + " # conventionally, use \"time\" as x-axis\n", + " ds[var_2d].plot(x=\"time\")\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "7554b2a6", + "metadata": {}, + "source": [ + "#### qc-plotting (optional)\n", + "\n", + "Note: act qc plotting has more strict requirement, one of them is the associated qc variable need to have \"flag_masks\" attributes. Which is added by using ds = act.io.armfiles.read_netcdf(files_list), then ds.clean.cleanup()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71c2096f", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " ds_act = act.io.armfiles.read_netcdf(full_path)\n", + " print(type(ds_act))\n", + " ds_act.clean.cleanup()\n", + "\n", + " # or \n", + " # ds.clean.cleanup()\n", + "except Exception as e:\n", + " print(\"ERROR\", e)\n", + " ds_act = ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "844f8505", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter valid variables for ACT qc plotting\n", + "condition = (\"qc_\" + df_info.var_name).apply(lambda x: ds_act[x].attrs.get(\"flag_masks\") is not None \n", + " if x in list(ds.variables) else False)\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_3 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings))) &\n", + " condition\n", + " ]\n", + "df_filter_3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd38cc6", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "try:\n", + " qc_variable = df_filter_3.var_name.values[0]\n", + " print(qc_variable)\n", + "except Exception as e:\n", + " print(e)\n", + " \n", + "try:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + " qc_ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "d76e4d27", + "metadata": {}, + "source": [ + "#### bonus: choose variables to plot from a dropdown menu " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb733804", + "metadata": {}, + "outputs": [], + "source": [ + "# Valid variables filtering\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_4 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82d5c20d", + "metadata": {}, + "outputs": [], + "source": [ + "# example 1: using xarray plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2fc9aaa4", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# fig, ax = plt.subplots(figsize=(10, 4))\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "# fig.clear() # Remove old lines from plot and plot new one\n", + "# if len(ds[var].dims)==2:\n", + "# ds[var].plot(x=\"time\", add_colorbar=False)\n", + "# else:\n", + "# ds[var].plot()\n", + "# plt.grid()\n", + "# plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fabdd802", + "metadata": {}, + "outputs": [], + "source": [ + "# example 2: using act plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "962b4186", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "\n", + "\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "\n", + "# i_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + "# i_display.add_subplots((1,), figsize=(10, 4))\n", + "# ax = i_display.plot(var, subplot_index=(0,), set_title=f\"{var} ({ds_act[var].attrs['long_name']})\",)\n", + "\n", + "# ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "# ax.grid()\n", + "# plt.show()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/MERGESONDE/mergesonde1mace.c1.ipynb b/VAPs/quicklook/MERGESONDE/mergesonde1mace.c1.ipynb new file mode 100644 index 00000000..b8abfc16 --- /dev/null +++ b/VAPs/quicklook/MERGESONDE/mergesonde1mace.c1.ipynb @@ -0,0 +1,339 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# MERGESONDE1MACE.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/mergesonde) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'mergesonde1mace'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2007-12-31', 'facility': 'M1', 'site': 'fkb', 'start_date': '2007-04-01'}, {'end_date': '2008-12-28', 'facility': 'M1', 'site': 'hfe', 'start_date': '2008-05-15'}, {'end_date': '2012-02-09', 'facility': 'M1', 'site': 'gan', 'start_date': '2011-10-01'}, {'end_date': '2010-12-30', 'facility': 'M1', 'site': 'grw', 'start_date': '2009-05-02'}, {'end_date': '2005-09-15', 'facility': 'M1', 'site': 'pye', 'start_date': '2005-04-20'}, {'end_date': '2015-06-29', 'facility': 'C1', 'site': 'nsa', 'start_date': '2001-04-01'}, {'end_date': '2006-12-31', 'facility': 'M1', 'site': 'nim', 'start_date': '2006-01-10'}, {'end_date': '2015-06-29', 'facility': 'C1', 'site': 'sgp', 'start_date': '1996-07-15'}, {'end_date': '2014-06-29', 'facility': 'C1', 'site': 'twp', 'start_date': '2000-01-01'}, {'end_date': '2013-09-08', 'facility': 'C2', 'site': 'twp', 'start_date': '2002-01-01'}, {'end_date': '2015-01-05', 'facility': 'C3', 'site': 'twp', 'start_date': '2011-01-01'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2015-06-27'\n", + "date_end = '2015-06-29'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['temp', 'rh', 'vap_pres']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'precip'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'temp'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/MERGESONDE/mergesonde2mace.c1.ipynb b/VAPs/quicklook/MERGESONDE/mergesonde2mace.c1.ipynb new file mode 100644 index 00000000..8c73a979 --- /dev/null +++ b/VAPs/quicklook/MERGESONDE/mergesonde2mace.c1.ipynb @@ -0,0 +1,339 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# MERGESONDE2MACE.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/mergesonde) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'mergesonde2mace'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2005-09-15', 'facility': 'M1', 'site': 'pye', 'start_date': '2005-04-20'}, {'end_date': '2007-01-07', 'facility': 'M1', 'site': 'nim', 'start_date': '2006-01-10'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'pye', 'M1' )\n", + "\n", + "date_start = '2005-09-13'\n", + "date_end = '2005-09-15'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['temp', 'rh', 'vap_pres']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'precip'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'temp'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/MFRSRCLDOD/.ipynb_checkpoints/mfrsrcldod1min.c1-checkpoint.ipynb b/VAPs/quicklook/MFRSRCLDOD/.ipynb_checkpoints/mfrsrcldod1min.c1-checkpoint.ipynb new file mode 100644 index 00000000..41b70061 --- /dev/null +++ b/VAPs/quicklook/MFRSRCLDOD/.ipynb_checkpoints/mfrsrcldod1min.c1-checkpoint.ipynb @@ -0,0 +1,799 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# MFRSRCLDOD1MIN.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/mfrsrcldod) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'mfrsrcldod1min'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2017-11-01', 'facility': 'M1', 'site': 'asi', 'start_date': '2016-05-02'}, {'end_date': '2019-04-30', 'facility': 'M1', 'site': 'cor', 'start_date': '2018-09-18'}, {'end_date': '2013-07-01', 'facility': 'M1', 'site': 'pvc', 'start_date': '2012-07-09'}, {'end_date': '2015-08-29', 'facility': 'M1', 'site': 'mao', 'start_date': '2015-04-17'}, {'end_date': '2012-04-01', 'facility': 'M1', 'site': 'pgh', 'start_date': '2011-07-19'}, {'end_date': '2012-02-05', 'facility': 'M1', 'site': 'gan', 'start_date': '2011-10-09'}, {'end_date': '2010-12-29', 'facility': 'M1', 'site': 'grw', 'start_date': '2009-05-04'}, {'end_date': '2018-03-13', 'facility': 'S1', 'site': 'mcq', 'start_date': '2016-04-01'}, {'end_date': '2007-11-15', 'facility': 'M1', 'site': 'fkb', 'start_date': '2007-05-07'}, {'end_date': '2019-10-27', 'facility': 'C1', 'site': 'ena', 'start_date': '2014-06-01'}, {'end_date': '2005-09-15', 'facility': 'M1', 'site': 'pye', 'start_date': '2005-02-19'}, {'end_date': '2021-01-29', 'facility': 'C1', 'site': 'sgp', 'start_date': '1998-01-01'}, {'end_date': '2011-10-19', 'facility': 'E10', 'site': 'sgp', 'start_date': '1997-10-31'}, {'end_date': '2021-09-21', 'facility': 'E11', 'site': 'sgp', 'start_date': '1997-08-23'}, {'end_date': '2021-09-21', 'facility': 'E12', 'site': 'sgp', 'start_date': '2001-07-24'}, {'end_date': '2022-06-30', 'facility': 'E13', 'site': 'sgp', 'start_date': '1998-07-10'}, {'end_date': '2021-09-21', 'facility': 'E15', 'site': 'sgp', 'start_date': '1997-09-10'}, {'end_date': '2011-11-15', 'facility': 'E16', 'site': 'sgp', 'start_date': '1997-08-21'}, {'end_date': '2009-11-17', 'facility': 'E18', 'site': 'sgp', 'start_date': '1997-10-17'}, {'end_date': '2011-05-23', 'facility': 'E19', 'site': 'sgp', 'start_date': '1998-07-09'}, {'end_date': '2009-10-14', 'facility': 'E1', 'site': 'sgp', 'start_date': '1997-11-21'}, {'end_date': '2011-11-17', 'facility': 'E20', 'site': 'sgp', 'start_date': '1999-04-24'}, {'end_date': '2009-12-01', 'facility': 'E22', 'site': 'sgp', 'start_date': '1999-01-15'}, {'end_date': '2009-11-14', 'facility': 'E24', 'site': 'sgp', 'start_date': '1997-11-26'}, {'end_date': '2002-04-08', 'facility': 'E25', 'site': 'sgp', 'start_date': '1998-01-11'}, {'end_date': '2009-12-04', 'facility': 'E27', 'site': 'sgp', 'start_date': '2003-12-30'}, {'end_date': '2009-10-20', 'facility': 'E2', 'site': 'sgp', 'start_date': '1997-11-05'}, {'end_date': '2021-06-22', 'facility': 'E31', 'site': 'sgp', 'start_date': '2011-11-26'}, {'end_date': '2021-09-21', 'facility': 'E32', 'site': 'sgp', 'start_date': '2011-11-26'}, {'end_date': '2021-09-21', 'facility': 'E33', 'site': 'sgp', 'start_date': '2011-09-27'}, {'end_date': '2021-09-21', 'facility': 'E34', 'site': 'sgp', 'start_date': '2011-09-28'}, {'end_date': '2021-09-21', 'facility': 'E35', 'site': 'sgp', 'start_date': '2011-09-28'}, {'end_date': '2021-09-21', 'facility': 'E36', 'site': 'sgp', 'start_date': '2011-10-18'}, {'end_date': '2021-09-21', 'facility': 'E37', 'site': 'sgp', 'start_date': '2011-11-02'}, {'end_date': '2017-10-15', 'facility': 'E38', 'site': 'sgp', 'start_date': '2011-12-15'}, {'end_date': '2009-10-28', 'facility': 'E3', 'site': 'sgp', 'start_date': '1998-07-24'}, {'end_date': '2011-09-26', 'facility': 'E4', 'site': 'sgp', 'start_date': '1997-12-20'}, {'end_date': '2009-11-02', 'facility': 'E5', 'site': 'sgp', 'start_date': '1998-03-22'}, {'end_date': '2011-10-18', 'facility': 'E6', 'site': 'sgp', 'start_date': '2003-12-19'}, {'end_date': '2011-11-14', 'facility': 'E7', 'site': 'sgp', 'start_date': '1999-07-12'}, {'end_date': '2009-11-10', 'facility': 'E8', 'site': 'sgp', 'start_date': '1997-09-03'}, {'end_date': '2021-09-21', 'facility': 'E9', 'site': 'sgp', 'start_date': '2008-03-25'}, {'end_date': '2014-06-04', 'facility': 'C1', 'site': 'twp', 'start_date': '1999-10-22'}, {'end_date': '2013-09-09', 'facility': 'C2', 'site': 'twp', 'start_date': '1999-09-08'}, {'end_date': '2014-10-05', 'facility': 'C3', 'site': 'twp', 'start_date': '2002-03-07'}]" + ] + }, + { + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "ac6764f5", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The following locations and date ranges are available for this VAP:\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
sitefacilitystart_dateend_date
0asiM12016-05-022017-11-01
1corM12018-09-182019-04-30
2pvcM12012-07-092013-07-01
3maoM12015-04-172015-08-29
4pghM12011-07-192012-04-01
5ganM12011-10-092012-02-05
6grwM12009-05-042010-12-29
7mcqS12016-04-012018-03-13
8fkbM12007-05-072007-11-15
9enaC12014-06-012019-10-27
10pyeM12005-02-192005-09-15
11sgpC11998-01-012021-01-29
12sgpE101997-10-312011-10-19
13sgpE111997-08-232021-09-21
14sgpE122001-07-242021-09-21
15sgpE131998-07-102022-06-30
16sgpE151997-09-102021-09-21
17sgpE161997-08-212011-11-15
18sgpE181997-10-172009-11-17
19sgpE191998-07-092011-05-23
20sgpE11997-11-212009-10-14
21sgpE201999-04-242011-11-17
22sgpE221999-01-152009-12-01
23sgpE241997-11-262009-11-14
24sgpE251998-01-112002-04-08
25sgpE272003-12-302009-12-04
26sgpE21997-11-052009-10-20
27sgpE312011-11-262021-06-22
28sgpE322011-11-262021-09-21
29sgpE332011-09-272021-09-21
30sgpE342011-09-282021-09-21
31sgpE352011-09-282021-09-21
32sgpE362011-10-182021-09-21
33sgpE372011-11-022021-09-21
34sgpE382011-12-152017-10-15
35sgpE31998-07-242009-10-28
36sgpE41997-12-202011-09-26
37sgpE51998-03-222009-11-02
38sgpE62003-12-192011-10-18
39sgpE71999-07-122011-11-14
40sgpE81997-09-032009-11-10
41sgpE92008-03-252021-09-21
42twpC11999-10-222014-06-04
43twpC21999-09-082013-09-09
44twpC32002-03-072014-10-05
\n", + "
" + ], + "text/plain": [ + " site facility start_date end_date\n", + "0 asi M1 2016-05-02 2017-11-01\n", + "1 cor M1 2018-09-18 2019-04-30\n", + "2 pvc M1 2012-07-09 2013-07-01\n", + "3 mao M1 2015-04-17 2015-08-29\n", + "4 pgh M1 2011-07-19 2012-04-01\n", + "5 gan M1 2011-10-09 2012-02-05\n", + "6 grw M1 2009-05-04 2010-12-29\n", + "7 mcq S1 2016-04-01 2018-03-13\n", + "8 fkb M1 2007-05-07 2007-11-15\n", + "9 ena C1 2014-06-01 2019-10-27\n", + "10 pye M1 2005-02-19 2005-09-15\n", + "11 sgp C1 1998-01-01 2021-01-29\n", + "12 sgp E10 1997-10-31 2011-10-19\n", + "13 sgp E11 1997-08-23 2021-09-21\n", + "14 sgp E12 2001-07-24 2021-09-21\n", + "15 sgp E13 1998-07-10 2022-06-30\n", + "16 sgp E15 1997-09-10 2021-09-21\n", + "17 sgp E16 1997-08-21 2011-11-15\n", + "18 sgp E18 1997-10-17 2009-11-17\n", + "19 sgp E19 1998-07-09 2011-05-23\n", + "20 sgp E1 1997-11-21 2009-10-14\n", + "21 sgp E20 1999-04-24 2011-11-17\n", + "22 sgp E22 1999-01-15 2009-12-01\n", + "23 sgp E24 1997-11-26 2009-11-14\n", + "24 sgp E25 1998-01-11 2002-04-08\n", + "25 sgp E27 2003-12-30 2009-12-04\n", + "26 sgp E2 1997-11-05 2009-10-20\n", + "27 sgp E31 2011-11-26 2021-06-22\n", + "28 sgp E32 2011-11-26 2021-09-21\n", + "29 sgp E33 2011-09-27 2021-09-21\n", + "30 sgp E34 2011-09-28 2021-09-21\n", + "31 sgp E35 2011-09-28 2021-09-21\n", + "32 sgp E36 2011-10-18 2021-09-21\n", + "33 sgp E37 2011-11-02 2021-09-21\n", + "34 sgp E38 2011-12-15 2017-10-15\n", + "35 sgp E3 1998-07-24 2009-10-28\n", + "36 sgp E4 1997-12-20 2011-09-26\n", + "37 sgp E5 1998-03-22 2009-11-02\n", + "38 sgp E6 2003-12-19 2011-10-18\n", + "39 sgp E7 1999-07-12 2011-11-14\n", + "40 sgp E8 1997-09-03 2009-11-10\n", + "41 sgp E9 2008-03-25 2021-09-21\n", + "42 twp C1 1999-10-22 2014-06-04\n", + "43 twp C2 1999-09-08 2013-09-09\n", + "44 twp C3 2002-03-07 2014-10-05" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2021-01-27'\n", + "date_end = '2021-01-29'" + ] + }, + { + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'/data/archive/sgp/sgpmfrsrcldod1minC1.c1'" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['20210127', '20210128', '20210129']" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "51feea2e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['/data/archive/sgp/sgpmfrsrcldod1minC1.c1/sgpmfrsrcldod1minC1.c1.20210127.000000.cdf',\n", + " '/data/archive/sgp/sgpmfrsrcldod1minC1.c1/sgpmfrsrcldod1minC1.c1.20210128.000000.cdf',\n", + " '/data/archive/sgp/sgpmfrsrcldod1minC1.c1/sgpmfrsrcldod1minC1.c1.20210129.000000.cdf']" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [ + { + "ename": "ValueError", + "evalue": "cannot reindex or align along dimension 'n_Io' because of conflicting dimension sizes: {113, 114}", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[0;32mIn[7], line 3\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[38;5;66;03m# Load files as a single dataset\u001b[39;00m\n\u001b[1;32m 2\u001b[0m files_list \u001b[38;5;241m=\u001b[39m files_filter \n\u001b[0;32m----> 3\u001b[0m ds \u001b[38;5;241m=\u001b[39m \u001b[43mact\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mio\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43marmfiles\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mread_netcdf\u001b[49m\u001b[43m(\u001b[49m\u001b[43mfiles_list\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 4\u001b[0m ds\u001b[38;5;241m.\u001b[39mclean\u001b[38;5;241m.\u001b[39mcleanup()\n\u001b[1;32m 5\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;132;01m{\u001b[39;00m\u001b[38;5;28mlen\u001b[39m(files_list)\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m files loaded\u001b[39m\u001b[38;5;124m'\u001b[39m)\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/act/io/armfiles.py:168\u001b[0m, in \u001b[0;36mread_netcdf\u001b[0;34m(filenames, concat_dim, return_None, combine, decode_times, use_cftime, use_base_time, combine_attrs, cleanup_qc, keep_variables, **kwargs)\u001b[0m\n\u001b[1;32m 164\u001b[0m ds \u001b[38;5;241m=\u001b[39m xr\u001b[38;5;241m.\u001b[39mopen_mfdataset(filenames, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs)\n\u001b[1;32m 166\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m 167\u001b[0m \u001b[38;5;66;03m# When all else fails raise the orginal exception\u001b[39;00m\n\u001b[0;32m--> 168\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m exception\n\u001b[1;32m 170\u001b[0m \u001b[38;5;66;03m# If requested use base_time and time_offset to derive time. Assumes that the units\u001b[39;00m\n\u001b[1;32m 171\u001b[0m \u001b[38;5;66;03m# of both are in seconds and that the value is number of seconds since epoch.\u001b[39;00m\n\u001b[1;32m 172\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m use_base_time:\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/act/io/armfiles.py:143\u001b[0m, in \u001b[0;36mread_netcdf\u001b[0;34m(filenames, concat_dim, return_None, combine, decode_times, use_cftime, use_base_time, combine_attrs, cleanup_qc, keep_variables, **kwargs)\u001b[0m\n\u001b[1;32m 139\u001b[0m except_tuple \u001b[38;5;241m=\u001b[39m except_tuple \u001b[38;5;241m+\u001b[39m (\u001b[38;5;167;01mFileNotFoundError\u001b[39;00m, \u001b[38;5;167;01mOSError\u001b[39;00m)\n\u001b[1;32m 141\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[1;32m 142\u001b[0m \u001b[38;5;66;03m# Read data file with Xarray function\u001b[39;00m\n\u001b[0;32m--> 143\u001b[0m ds \u001b[38;5;241m=\u001b[39m \u001b[43mxr\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mopen_mfdataset\u001b[49m\u001b[43m(\u001b[49m\u001b[43mfilenames\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 145\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m except_tuple \u001b[38;5;28;01mas\u001b[39;00m exception:\n\u001b[1;32m 146\u001b[0m \u001b[38;5;66;03m# If requested return None for File not found error\u001b[39;00m\n\u001b[1;32m 147\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mtype\u001b[39m(exception)\u001b[38;5;241m.\u001b[39m\u001b[38;5;18m__name__\u001b[39m \u001b[38;5;241m==\u001b[39m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mFileNotFoundError\u001b[39m\u001b[38;5;124m'\u001b[39m:\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/backends/api.py:1026\u001b[0m, in \u001b[0;36mopen_mfdataset\u001b[0;34m(paths, chunks, concat_dim, compat, preprocess, engine, data_vars, coords, combine, parallel, join, attrs_file, combine_attrs, **kwargs)\u001b[0m\n\u001b[1;32m 1013\u001b[0m combined \u001b[38;5;241m=\u001b[39m _nested_combine(\n\u001b[1;32m 1014\u001b[0m datasets,\n\u001b[1;32m 1015\u001b[0m concat_dims\u001b[38;5;241m=\u001b[39mconcat_dim,\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 1021\u001b[0m combine_attrs\u001b[38;5;241m=\u001b[39mcombine_attrs,\n\u001b[1;32m 1022\u001b[0m )\n\u001b[1;32m 1023\u001b[0m \u001b[38;5;28;01melif\u001b[39;00m combine \u001b[38;5;241m==\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mby_coords\u001b[39m\u001b[38;5;124m\"\u001b[39m:\n\u001b[1;32m 1024\u001b[0m \u001b[38;5;66;03m# Redo ordering from coordinates, ignoring how they were ordered\u001b[39;00m\n\u001b[1;32m 1025\u001b[0m \u001b[38;5;66;03m# previously\u001b[39;00m\n\u001b[0;32m-> 1026\u001b[0m combined \u001b[38;5;241m=\u001b[39m \u001b[43mcombine_by_coords\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 1027\u001b[0m \u001b[43m \u001b[49m\u001b[43mdatasets\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1028\u001b[0m \u001b[43m \u001b[49m\u001b[43mcompat\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcompat\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1029\u001b[0m \u001b[43m \u001b[49m\u001b[43mdata_vars\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mdata_vars\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1030\u001b[0m \u001b[43m \u001b[49m\u001b[43mcoords\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcoords\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1031\u001b[0m \u001b[43m \u001b[49m\u001b[43mjoin\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mjoin\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1032\u001b[0m \u001b[43m \u001b[49m\u001b[43mcombine_attrs\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcombine_attrs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1033\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 1034\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m 1035\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\n\u001b[1;32m 1036\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;132;01m{}\u001b[39;00m\u001b[38;5;124m is an invalid option for the keyword argument\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 1037\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m ``combine``\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;241m.\u001b[39mformat(combine)\n\u001b[1;32m 1038\u001b[0m )\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/core/combine.py:982\u001b[0m, in \u001b[0;36mcombine_by_coords\u001b[0;34m(data_objects, compat, data_vars, coords, fill_value, join, combine_attrs, datasets)\u001b[0m\n\u001b[1;32m 980\u001b[0m concatenated_grouped_by_data_vars \u001b[38;5;241m=\u001b[39m []\n\u001b[1;32m 981\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m \u001b[38;5;28mvars\u001b[39m, datasets_with_same_vars \u001b[38;5;129;01min\u001b[39;00m grouped_by_vars:\n\u001b[0;32m--> 982\u001b[0m concatenated \u001b[38;5;241m=\u001b[39m \u001b[43m_combine_single_variable_hypercube\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 983\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43mlist\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mdatasets_with_same_vars\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 984\u001b[0m \u001b[43m \u001b[49m\u001b[43mfill_value\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mfill_value\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 985\u001b[0m \u001b[43m \u001b[49m\u001b[43mdata_vars\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mdata_vars\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 986\u001b[0m \u001b[43m \u001b[49m\u001b[43mcoords\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcoords\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 987\u001b[0m \u001b[43m \u001b[49m\u001b[43mcompat\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcompat\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 988\u001b[0m \u001b[43m \u001b[49m\u001b[43mjoin\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mjoin\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 989\u001b[0m \u001b[43m \u001b[49m\u001b[43mcombine_attrs\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcombine_attrs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 990\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 991\u001b[0m concatenated_grouped_by_data_vars\u001b[38;5;241m.\u001b[39mappend(concatenated)\n\u001b[1;32m 993\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m merge(\n\u001b[1;32m 994\u001b[0m concatenated_grouped_by_data_vars,\n\u001b[1;32m 995\u001b[0m compat\u001b[38;5;241m=\u001b[39mcompat,\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 998\u001b[0m combine_attrs\u001b[38;5;241m=\u001b[39mcombine_attrs,\n\u001b[1;32m 999\u001b[0m )\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/core/combine.py:640\u001b[0m, in \u001b[0;36m_combine_single_variable_hypercube\u001b[0;34m(datasets, fill_value, data_vars, coords, compat, join, combine_attrs)\u001b[0m\n\u001b[1;32m 637\u001b[0m _check_dimension_depth_tile_ids(combined_ids)\n\u001b[1;32m 639\u001b[0m \u001b[38;5;66;03m# Concatenate along all of concat_dims one by one to create single ds\u001b[39;00m\n\u001b[0;32m--> 640\u001b[0m concatenated \u001b[38;5;241m=\u001b[39m \u001b[43m_combine_nd\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 641\u001b[0m \u001b[43m \u001b[49m\u001b[43mcombined_ids\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 642\u001b[0m \u001b[43m \u001b[49m\u001b[43mconcat_dims\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mconcat_dims\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 643\u001b[0m \u001b[43m \u001b[49m\u001b[43mdata_vars\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mdata_vars\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 644\u001b[0m \u001b[43m \u001b[49m\u001b[43mcoords\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcoords\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 645\u001b[0m \u001b[43m \u001b[49m\u001b[43mcompat\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcompat\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 646\u001b[0m \u001b[43m \u001b[49m\u001b[43mfill_value\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mfill_value\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 647\u001b[0m \u001b[43m \u001b[49m\u001b[43mjoin\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mjoin\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 648\u001b[0m \u001b[43m \u001b[49m\u001b[43mcombine_attrs\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcombine_attrs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 649\u001b[0m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 651\u001b[0m \u001b[38;5;66;03m# Check the overall coordinates are monotonically increasing\u001b[39;00m\n\u001b[1;32m 652\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m dim \u001b[38;5;129;01min\u001b[39;00m concat_dims:\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/core/combine.py:239\u001b[0m, in \u001b[0;36m_combine_nd\u001b[0;34m(combined_ids, concat_dims, data_vars, coords, compat, fill_value, join, combine_attrs)\u001b[0m\n\u001b[1;32m 235\u001b[0m \u001b[38;5;66;03m# Each iteration of this loop reduces the length of the tile_ids tuples\u001b[39;00m\n\u001b[1;32m 236\u001b[0m \u001b[38;5;66;03m# by one. It always combines along the first dimension, removing the first\u001b[39;00m\n\u001b[1;32m 237\u001b[0m \u001b[38;5;66;03m# element of the tuple\u001b[39;00m\n\u001b[1;32m 238\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m concat_dim \u001b[38;5;129;01min\u001b[39;00m concat_dims:\n\u001b[0;32m--> 239\u001b[0m combined_ids \u001b[38;5;241m=\u001b[39m \u001b[43m_combine_all_along_first_dim\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 240\u001b[0m \u001b[43m \u001b[49m\u001b[43mcombined_ids\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 241\u001b[0m \u001b[43m \u001b[49m\u001b[43mdim\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mconcat_dim\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 242\u001b[0m \u001b[43m \u001b[49m\u001b[43mdata_vars\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mdata_vars\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 243\u001b[0m \u001b[43m \u001b[49m\u001b[43mcoords\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcoords\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 244\u001b[0m \u001b[43m \u001b[49m\u001b[43mcompat\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcompat\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 245\u001b[0m \u001b[43m \u001b[49m\u001b[43mfill_value\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mfill_value\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 246\u001b[0m \u001b[43m \u001b[49m\u001b[43mjoin\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mjoin\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 247\u001b[0m \u001b[43m \u001b[49m\u001b[43mcombine_attrs\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcombine_attrs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 248\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 249\u001b[0m (combined_ds,) \u001b[38;5;241m=\u001b[39m combined_ids\u001b[38;5;241m.\u001b[39mvalues()\n\u001b[1;32m 250\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m combined_ds\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/core/combine.py:275\u001b[0m, in \u001b[0;36m_combine_all_along_first_dim\u001b[0;34m(combined_ids, dim, data_vars, coords, compat, fill_value, join, combine_attrs)\u001b[0m\n\u001b[1;32m 273\u001b[0m combined_ids \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mdict\u001b[39m(\u001b[38;5;28msorted\u001b[39m(group))\n\u001b[1;32m 274\u001b[0m datasets \u001b[38;5;241m=\u001b[39m combined_ids\u001b[38;5;241m.\u001b[39mvalues()\n\u001b[0;32m--> 275\u001b[0m new_combined_ids[new_id] \u001b[38;5;241m=\u001b[39m \u001b[43m_combine_1d\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 276\u001b[0m \u001b[43m \u001b[49m\u001b[43mdatasets\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mdim\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mcompat\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mdata_vars\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mcoords\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mfill_value\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mjoin\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mcombine_attrs\u001b[49m\n\u001b[1;32m 277\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 278\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m new_combined_ids\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/core/combine.py:298\u001b[0m, in \u001b[0;36m_combine_1d\u001b[0;34m(datasets, concat_dim, compat, data_vars, coords, fill_value, join, combine_attrs)\u001b[0m\n\u001b[1;32m 296\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m concat_dim \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[1;32m 297\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m--> 298\u001b[0m combined \u001b[38;5;241m=\u001b[39m \u001b[43mconcat\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 299\u001b[0m \u001b[43m \u001b[49m\u001b[43mdatasets\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 300\u001b[0m \u001b[43m \u001b[49m\u001b[43mdim\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mconcat_dim\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 301\u001b[0m \u001b[43m \u001b[49m\u001b[43mdata_vars\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mdata_vars\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 302\u001b[0m \u001b[43m \u001b[49m\u001b[43mcoords\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcoords\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 303\u001b[0m \u001b[43m \u001b[49m\u001b[43mcompat\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcompat\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 304\u001b[0m \u001b[43m \u001b[49m\u001b[43mfill_value\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mfill_value\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 305\u001b[0m \u001b[43m \u001b[49m\u001b[43mjoin\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mjoin\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 306\u001b[0m \u001b[43m \u001b[49m\u001b[43mcombine_attrs\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcombine_attrs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 307\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 308\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m err:\n\u001b[1;32m 309\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mencountered unexpected variable\u001b[39m\u001b[38;5;124m\"\u001b[39m \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mstr\u001b[39m(err):\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/core/concat.py:248\u001b[0m, in \u001b[0;36mconcat\u001b[0;34m(objs, dim, data_vars, coords, compat, positions, fill_value, join, combine_attrs)\u001b[0m\n\u001b[1;32m 236\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m _dataarray_concat(\n\u001b[1;32m 237\u001b[0m objs,\n\u001b[1;32m 238\u001b[0m dim\u001b[38;5;241m=\u001b[39mdim,\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 245\u001b[0m combine_attrs\u001b[38;5;241m=\u001b[39mcombine_attrs,\n\u001b[1;32m 246\u001b[0m )\n\u001b[1;32m 247\u001b[0m \u001b[38;5;28;01melif\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(first_obj, Dataset):\n\u001b[0;32m--> 248\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43m_dataset_concat\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 249\u001b[0m \u001b[43m \u001b[49m\u001b[43mobjs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 250\u001b[0m \u001b[43m \u001b[49m\u001b[43mdim\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mdim\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 251\u001b[0m \u001b[43m \u001b[49m\u001b[43mdata_vars\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mdata_vars\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 252\u001b[0m \u001b[43m \u001b[49m\u001b[43mcoords\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcoords\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 253\u001b[0m \u001b[43m \u001b[49m\u001b[43mcompat\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcompat\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 254\u001b[0m \u001b[43m \u001b[49m\u001b[43mpositions\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mpositions\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 255\u001b[0m \u001b[43m \u001b[49m\u001b[43mfill_value\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mfill_value\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 256\u001b[0m \u001b[43m \u001b[49m\u001b[43mjoin\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mjoin\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 257\u001b[0m \u001b[43m \u001b[49m\u001b[43mcombine_attrs\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcombine_attrs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 258\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 259\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m 260\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mTypeError\u001b[39;00m(\n\u001b[1;32m 261\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mcan only concatenate xarray Dataset and DataArray \u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 262\u001b[0m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mobjects, got \u001b[39m\u001b[38;5;132;01m{\u001b[39;00m\u001b[38;5;28mtype\u001b[39m(first_obj)\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 263\u001b[0m )\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/core/concat.py:471\u001b[0m, in \u001b[0;36m_dataset_concat\u001b[0;34m(datasets, dim, data_vars, coords, compat, positions, fill_value, join, combine_attrs)\u001b[0m\n\u001b[1;32m 468\u001b[0m \u001b[38;5;66;03m# Make sure we're working on a copy (we'll be loading variables)\u001b[39;00m\n\u001b[1;32m 469\u001b[0m datasets \u001b[38;5;241m=\u001b[39m [ds\u001b[38;5;241m.\u001b[39mcopy() \u001b[38;5;28;01mfor\u001b[39;00m ds \u001b[38;5;129;01min\u001b[39;00m datasets]\n\u001b[1;32m 470\u001b[0m datasets \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mlist\u001b[39m(\n\u001b[0;32m--> 471\u001b[0m \u001b[43malign\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mdatasets\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mjoin\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mjoin\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mcopy\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43;01mFalse\u001b[39;49;00m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mexclude\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43m[\u001b[49m\u001b[43mdim\u001b[49m\u001b[43m]\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mfill_value\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mfill_value\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 472\u001b[0m )\n\u001b[1;32m 474\u001b[0m dim_coords, dims_sizes, coord_names, data_names \u001b[38;5;241m=\u001b[39m _parse_datasets(datasets)\n\u001b[1;32m 475\u001b[0m dim_names \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mset\u001b[39m(dim_coords)\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/core/alignment.py:797\u001b[0m, in \u001b[0;36malign\u001b[0;34m(join, copy, indexes, exclude, fill_value, *objects)\u001b[0m\n\u001b[1;32m 601\u001b[0m \u001b[38;5;250m\u001b[39m\u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[1;32m 602\u001b[0m \u001b[38;5;124;03mGiven any number of Dataset and/or DataArray objects, returns new\u001b[39;00m\n\u001b[1;32m 603\u001b[0m \u001b[38;5;124;03mobjects with aligned indexes and dimension sizes.\u001b[39;00m\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 787\u001b[0m \n\u001b[1;32m 788\u001b[0m \u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[1;32m 789\u001b[0m aligner \u001b[38;5;241m=\u001b[39m Aligner(\n\u001b[1;32m 790\u001b[0m objects,\n\u001b[1;32m 791\u001b[0m join\u001b[38;5;241m=\u001b[39mjoin,\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 795\u001b[0m fill_value\u001b[38;5;241m=\u001b[39mfill_value,\n\u001b[1;32m 796\u001b[0m )\n\u001b[0;32m--> 797\u001b[0m \u001b[43maligner\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43malign\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 798\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m aligner\u001b[38;5;241m.\u001b[39mresults\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/core/alignment.py:585\u001b[0m, in \u001b[0;36mAligner.align\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 583\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39massert_no_index_conflict()\n\u001b[1;32m 584\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39malign_indexes()\n\u001b[0;32m--> 585\u001b[0m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43massert_unindexed_dim_sizes_equal\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 587\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mjoin \u001b[38;5;241m==\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124moverride\u001b[39m\u001b[38;5;124m\"\u001b[39m:\n\u001b[1;32m 588\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39moverride_indexes()\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/core/alignment.py:484\u001b[0m, in \u001b[0;36mAligner.assert_unindexed_dim_sizes_equal\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 482\u001b[0m add_err_msg \u001b[38;5;241m=\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 483\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mlen\u001b[39m(sizes) \u001b[38;5;241m>\u001b[39m \u001b[38;5;241m1\u001b[39m:\n\u001b[0;32m--> 484\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\n\u001b[1;32m 485\u001b[0m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mcannot reindex or align along dimension \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mdim\u001b[38;5;132;01m!r}\u001b[39;00m\u001b[38;5;124m \u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 486\u001b[0m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mbecause of conflicting dimension sizes: \u001b[39m\u001b[38;5;132;01m{\u001b[39;00msizes\u001b[38;5;132;01m!r}\u001b[39;00m\u001b[38;5;124m\"\u001b[39m \u001b[38;5;241m+\u001b[39m add_err_msg\n\u001b[1;32m 487\u001b[0m )\n", + "\u001b[0;31mValueError\u001b[0m: cannot reindex or align along dimension 'n_Io' because of conflicting dimension sizes: {113, 114}" + ] + } + ], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['optical_depth_instantaneous']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'optical_depth_instantaneous'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'optical_depth_instantaneous'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/MFRSRCLDOD/MFRSRCLDOD_tutorial.ipynb b/VAPs/quicklook/MFRSRCLDOD/MFRSRCLDOD_tutorial.ipynb new file mode 100644 index 00000000..ecd26e71 --- /dev/null +++ b/VAPs/quicklook/MFRSRCLDOD/MFRSRCLDOD_tutorial.ipynb @@ -0,0 +1,838 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# MFRSRCLDOD1MIN.C1 Notebook\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/mfrsrcldod) for more information about this vap." + ] + }, + { + "cell_type": "markdown", + "id": "97097763", + "metadata": {}, + "source": [ + "In this notebook, we demonstrate the workflow to explore ARM vap data (using mfrsrcldod1min as an example.) Value-added products (VAPs) are higher-order data products that have been analyzed and processed to ease scientists’ use of ARM data in atmospheric research and global climate models. \n", + "Here is the main content we will cover." + ] + }, + { + "cell_type": "markdown", + "id": "eddec40f", + "metadata": {}, + "source": [ + "# Table of Content\n", + "## Access the data\n", + "* How to retrieve the data\n", + "* Data path and file name conventions\n", + "* Load data\n", + "## Explore the data\n", + "* NetCDF Data structure\n", + "* Xarray essentials\n", + "* Xarray Variable\n", + "## Plot the data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "import random\n", + "\n", + "import glob\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "2372d5be", + "metadata": {}, + "source": [ + "## Access the data" + ] + }, + { + "cell_type": "markdown", + "id": "5b0d9684", + "metadata": {}, + "source": [ + "### How to retrieve the data\n", + "We assume the path \"/data/archive\" is available where you are running this notebook. You can use `os.path.exists(\"/data/archive\")` to verify if the path exists at your machine.\n", + "\n", + "\n", + "### Data path and file name conventions\n", + "There several common terminologies regarding ARM data, for example, data-stream-name, data-level, site, facility, instrument, etc. (For more details, please see [ARM Data File Standards Version 1.2](https://www.google.com/search?q=arm+datastream+facility+cite+definition&rlz=1C1GCEJ_enUS1029US1029&ei=hb41ZICrBPukqtsPvMODIA&ved=0ahUKEwjAgeiS0aL-AhV7kmoFHbzhAAQQ4dUDCBA&uact=5&oq=arm+datastream+facility+cite+definition&gs_lcp=Cgxnd3Mtd2l6LXNlcnAQAzIFCAAQogQyBQgAEKIEMgUIABCiBDIFCAAQogQyBQgAEKIEOgoIABBHENYEELADOgoIIRCgARDDBBAKSgQIQRgAUJ4DWIoOYO0PaAFwAXgAgAGMAYgBuQmSAQMyLjmYAQCgAQHIAQjAAQE&sclient=gws-wiz-serp).) \n", + "\n", + "For example, this notebook is called `mfrsrcldod1min.c1`, where `mfrsrcldod1min` is the \"datastream name\", and the `{process.ds_class_level}}` is the \"data level\".\n", + "\n", + "This datastream also contains site `asi` and facility `M1`. (Note: individual datastream might have multiple site-facility pairs.)\n", + "In such a case, the data of this data-stream is stored at `/data/archive/asi/asimfrsrcldod1minM1.c1`, which is in the format of `//.`. We can use the following method to assign the data-stream directory `datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )`\n", + "\n", + "The data files under datastream_dir also follows naming conventions. But once reach the datastream_dir level, the most import file naming convention to differentiate the files is \"yyyyMMdd.hhmmss\", which comes handy to filter out files based on datetime. For example, we can use `glob.glob(f'{datastream_dir}/*.200709*.*')` to filter files in 2007 September.\n", + "\n", + "(Note: refer to the https://adc.arm.gov/discovery/#/ and https://adc.arm.gov/solr8/metadata/select API to explore ARM datastream and assoicated available sites and facilities\n", + "\n", + "Please see the following examples in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d7e9eb85", + "metadata": {}, + "outputs": [], + "source": [ + "# Verify if DATA_DIR path exists\n", + "DATA_DIR = \"/data/archive\"\n", + "os.path.exists(DATA_DIR)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "586993fd", + "metadata": {}, + "outputs": [], + "source": [ + "# Speicify datastream_dir following the path conventions and check its existence\n", + "DATASTREAM_NAME = \"mfrsrcldod1min\"\n", + "DATA_LEVEL = \"c1\"\n", + "site = \"asi\"\n", + "facility = \"M1\"\n", + "datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )\n", + "print(datastream_dir)\n", + "print(os.path.exists(datastream_dir))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0742f7c1", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: list 5 (random) files under datastream_dir\n", + "files = os.listdir(datastream_dir)\n", + "files[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39b98a36", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: get most recent file\n", + "list_of_files = glob.glob(f\"{datastream_dir}/*\") # * means all if need specific format then *.csv\n", + "latest_file = max(list_of_files, key=os.path.getctime)\n", + "latest_file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "902d514e", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus sort datastream files based on datetime\n", + "files = os.listdir(datastream_dir)\n", + "file_sorted = files.copy()\n", + "file_sorted.sort() \n", + "print(file_sorted[:5])\n", + "\n", + "# to reverse\n", + "file_sorted_reverse = files.copy() \n", + "file_sorted_reverse.sort(reverse=True)\n", + "print(file_sorted_reverse[:5])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec5923b2", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: pattern matching\n", + "# filter the 200709** files under datastream_dir\n", + "files_filter = glob.glob(f'{datastream_dir}/*.200709*.*')\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c4a4aa18", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: use armnotebook_utils.file_filter (TODO) to filter files based on datastream info \n" + ] + }, + { + "cell_type": "markdown", + "id": "cfeb9efc", + "metadata": {}, + "source": [ + "### Load data\n", + "The arm data is stored in [NetCDF](https://en.wikipedia.org/wiki/NetCDF#:~:text=%22NetCDF%20(network%20Common%20Data%20Form,format%20for%20representing%20scientific%20data.) format. We can use xarray's `open_dataset` method to load single file, or `open_mfdataset` to open multiple files. (Note: the latter will still return a single xarray dataset object by combining multiple files.)\n", + "\n", + "Note: open_dataset keeps the file handle open and lazy loads its contents. All parameters are passed directly to open_dataset. It is a preferable method over load_dataset for memory-effiency. (more details, please see [xarray.open_dataset](https://docs.xarray.dev/en/stable/generated/xarray.open_dataset.html))\n", + "\n", + "See the following example in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a440a329", + "metadata": {}, + "outputs": [], + "source": [ + "# open single file\n", + "full_path = latest_file\n", + "print(full_path)\n", + "ds_single = xr.open_dataset(full_path)\n", + "ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0143a3d", + "metadata": {}, + "outputs": [], + "source": [ + "print(ds_single)\n", + "print(type(ds_single))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c0f8939", + "metadata": {}, + "outputs": [], + "source": [ + "# open multiple files\n", + "n_files = 3\n", + "full_paths = [os.path.join(datastream_dir, f_path) for f_path in file_sorted_reverse[:n_files]]\n", + "print(full_paths)\n", + "try: # Note: sometimes multiple files cannot be merged, so we used try except here.\n", + " ds_mutiple = xr.open_mfdataset(full_paths)\n", + " # ds_mutiple\n", + " print(type(ds_mutiple))\n", + " print(ds_mutiple)\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "3fa59943", + "metadata": {}, + "source": [ + "## Explore the data" + ] + }, + { + "cell_type": "markdown", + "id": "571f69b9", + "metadata": {}, + "source": [ + "### NetCDF Data structure \n", + "\n", + "\n", + "(If you are confident with NetCDF basics and xarray essentials, feel free to skip this session.)\n", + "Before we dive into data exploration, there are some eseential concpets we should be familiar with (shown below): \n", + "* Dataset\n", + "* Data array\n", + "* Variable\n", + "* Dimenssion\n", + "* Coordinate\n", + "* Data Type\n", + "* Meta Data (Attributes)\n", + "\n", + "We will not go into details about NetCDF basics and here are some references you might find helpful\n", + "* [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#)\n", + "* [Network Common Data Form (NetCDF)](https://www.unidata.ucar.edu/software/netcdf/) and [A Brief History of (netCDF) Time](https://www.unidata.ucar.edu/software/netcdf/time/recs.html)\n", + "\n", + "\n", + "\n", + "### Xarray essentials\n", + "Earlier we introduced the xarray pacakge and used `open_dataset` and `open_mfdataset` to retrieve NetCDF data file as an xarray Dataset object. Recall that you can review the data in a notebook by using `print(ds)` or `ds`. \n", + "\n", + "Using xarray to retrieve the aforementioned NetCDF basics is straightforwared. In general,\n", + "* Dataset: `ds`\n", + "* Data array: `ds.variables`\n", + "* Variable: `ds.ds.variables`\n", + "* Dimenssion: `ds.dims`\n", + "* Coordinate: `ds.coords`\n", + "* Data Type: `type`\n", + "* Meta Data (Attributes)\n", + "\n", + "Also, here are some references if you are new to xarray\n", + "* [Xarray in 45 minutes](https://tutorial.xarray.dev/overview/xarray-in-45-min.html)\n", + "* [Handling NetCDF Files using XArray for Absolute Beginners](https://towardsdatascience.com/handling-netcdf-files-using-xarray-for-absolute-beginners-111a8ab4463f)\n", + "\n", + "Try the following commands in action\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b3eac323", + "metadata": {}, + "outputs": [], + "source": [ + "ds = ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77ecf85d", + "metadata": {}, + "outputs": [], + "source": [ + "# Dataset \n", + "ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25e7de09", + "metadata": {}, + "outputs": [], + "source": [ + "# Data array, Variable\n", + "# Note: the info can be overwhelming. Do not attempt to grasp everything at the first glance.\n", + "ds.variables" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c41b67e", + "metadata": {}, + "outputs": [], + "source": [ + "# dimenssions\n", + "ds.dims" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "156f1dfc", + "metadata": {}, + "outputs": [], + "source": [ + "# coordinates\n", + "ds.coords" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "277d6064", + "metadata": {}, + "outputs": [], + "source": [ + "# Meta Data (Attributes)\n", + "ds.attrs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d334681f", + "metadata": {}, + "outputs": [], + "source": [ + "# type\n", + "print(type(ds))\n", + "print(type(ds.variables))\n", + "print(type(ds.dims))\n", + "print(type(ds.coords))\n", + "print(type(ds.attrs))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "643399d6", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: retrieve variable names only\n", + "list(ds.variables)" + ] + }, + { + "cell_type": "markdown", + "id": "1cf36878", + "metadata": {}, + "source": [ + "#### Discussion: variable vs. coordinates vs. dimenssions. \n", + "From [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#):\n", + "\n", + "* Dimession: A dimension may be used to represent a real physical dimension, for example, time, latitude, longitude, or height. A dimension might also be used to index other quantities, for example station or model-run-number.\n", + "A netCDF dimension has both a name and a length. A dimension length is an arbitrary positive integer, except that one dimension in a netCDF dataset can have the length UNLIMITED.\n", + "\n", + "* Variables: Variables are used to store the bulk of the data in a netCDF dataset. A variable represents an array of values of the same type. A scalar value is treated as a 0-dimensional array. A variable has a name, a data type, and a shape described by its list of dimensions specified when the variable is created. A variable may also have associated attributes, which may be added, deleted or changed after the variable is created.\n", + "\n", + "* Coordinate (Variables): It is legal for a variable to have the same name as a dimension. Such variables have no special meaning to the netCDF library. However there is a convention that such variables should be treated in a special way by software using this library.\n", + "A variable with the same name as a dimension is called a coordinate variable. It typically defines a physical coordinate corresponding to that dimension.\n", + "\n", + "\n", + "Tips: By definition, coordinates and dimenssions are also variable. For example ds.time is a coordinate but it is also a special variable. But in practice, when we talk about variable, it implies regular/non-coordinate variable. We will use this convention for the remaining notebook. " + ] + }, + { + "cell_type": "markdown", + "id": "d47120f9", + "metadata": {}, + "source": [ + "### Xarray Variable\n", + "\n", + "\n", + "We can use `ds.variables` to access varialbes of a dataset. But many times viewing print-out the whole datasets can be overwhemling. Instead, we would work on the individual variable (also called Xarray data array.) using the `ds[var_name]` syntax, where `var_name` is the name of the variable. See the following examples in action.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5a57e136", + "metadata": {}, + "outputs": [], + "source": [ + "print(type(ds[\"time\"]))\n", + "ds[\"time\"]" + ] + }, + { + "cell_type": "markdown", + "id": "51ec643e", + "metadata": {}, + "source": [ + "#### Variable properties\n", + "For individual varible, the following properties are mostly used with the assocaited xarray method to retrieve them. (assuming var = ds[var_name])\n", + "* name: `var.name`\n", + "* data content: `var.data`\n", + "* attributes: `var.attrs`\n", + "* dimenstions: `var.dims`\n", + "* data type: `var.data.dtype`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a632a525", + "metadata": {}, + "outputs": [], + "source": [ + "var_name = \"time\"\n", + "var = ds[var_name]\n", + "\n", + "print(\"var.name: \\n\", var.name, \"\\n\")\n", + "print(\"var.data: \\n\", var.data, \"\\n\")\n", + "print(\"var.attrs: \\n\", var.attrs, \"\\n\")\n", + "print(\"var.dims: \\n\", var.dims, \"\\n\")\n", + "print(\"var.data.dtype: \\n\", var.data.dtype, \"\\n\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b256b07f", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: indivdual variable info table (collect and display variable info as a tabular format)\n", + "df_info = pd.DataFrame()\n", + "df_info[\"var_name\"] = list(ds.variables)\n", + "df_info[\"dims\"] = df_info.var_name.apply(lambda x: ds[x].dims)\n", + "df_info[\"is_dim\"] = df_info.var_name.apply(lambda x: x in ds.dims)\n", + "df_info[\"n_dim\"] = df_info.var_name.apply(lambda x: len(ds[x].dims))\n", + "df_info[\"attrs\"] = df_info.var_name.apply(lambda x: ds[x].attrs)\n", + "df_info[\"dtype\"] = df_info.var_name.apply(lambda x: ds[x].data.dtype)\n", + "df_info" + ] + }, + { + "cell_type": "markdown", + "id": "e6a36b96", + "metadata": {}, + "source": [ + "### Data cleaning/Preprocessing (skipped)\n", + "Data cleaning and preprocessing is an important stage in the the data analysis pipeline. However it will be out of the scope of this notebook. For more information about data cleaning and preprocessing basics, here are some references. \n", + "* [Xarray Fundamentals](https://earth-env-data-science.github.io/lectures/xarray/xarray.html)\n", + "* [Xarray Tutorial — Pangeo Gallery documentation](http://gallery.pangeo.io/repos/pangeo-data/pangeo-tutorial-gallery/xarray.html)\n", + "* [Pythonic Data Cleaning With pandas and NumPy](https://realpython.com/python-data-cleaning-numpy-pandas/)\n", + "* [Pandas - Cleaning Data](https://www.w3schools.com/python/pandas/pandas_cleaning.asp)" + ] + }, + { + "cell_type": "markdown", + "id": "ae6e1a69", + "metadata": {}, + "source": [ + "### Plotting\n", + "Data visualization (or plotting) is another important data analysis topic and deserves its own discussion. Here in this notebook we will only demonstrate basic tool for simple data visualization tasks.\n", + "\n", + "Here are some reference you might find useful:\n", + "* xarray plotting: https://docs.xarray.dev/en/stable/user-guide/plotting.html\n", + "* Atmospheric Community Toolkit (ACT): https://arm-doe.github.io/ACT/index.html\n", + "\n", + "Note: this notebook is auto-generated using a template. It uses a general idea to select variable(s) to plot and is not customized to each indivdual notebook. Feel free to change the variables in intrrests, especially certain figure is failed to plot." + ] + }, + { + "cell_type": "markdown", + "id": "7e417fb2", + "metadata": {}, + "source": [ + "#### 1-dimenssional basic time series plot\n", + "\n", + "For the following plot we would like to find variables such that\n", + "* it has one and only one dimession\n", + "* \"time\" is its coordinate variable\n", + "* it is not a dimenssion itself,\n", + "* it is not a special variable with substrings within [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "\n", + "Please see the following example in action. (For more details about pandas filtering, please see the following references.)\n", + "* [pandas: multiple conditions while indexing data frame](https://stackoverflow.com/questions/22591174/pandas-multiple-conditions-while-indexing-data-frame-unexpected-behavior)\n", + "* [pandas.DataFrame.apply](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.apply.html)\n", + "* [How to test if a string contains one of the substrings in a list...](https://stackoverflow.com/questions/26577516/how-to-test-if-a-string-contains-one-of-the-substrings-in-a-list-in-pandas" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30edac2d", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==1, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter = df_info[(df_info.n_dim == 1) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4777a659", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_1d = df_filter.var_name.values[0]\n", + " var_1d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb5ae985", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " ds[var_1d].plot()\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "9e41066e", + "metadata": {}, + "source": [ + "#### 2-dimenssional basic plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09bd4adc", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==2, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_2 = df_info[(df_info.n_dim == 2) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2cdea1a9", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_2d = df_filter_2.var_name.values[0]\n", + " var_2d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5f48879", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " print(ds[var_2d].dims)\n", + " # ds[var_2d].plot()\n", + "\n", + " # conventionally, use \"time\" as x-axis\n", + " ds[var_2d].plot(x=\"time\")\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "7554b2a6", + "metadata": {}, + "source": [ + "#### qc-plotting (optional)\n", + "\n", + "Note: act qc plotting has more strict requirement, one of them is the associated qc variable need to have \"flag_masks\" attributes. Which is added by using ds = act.io.armfiles.read_netcdf(files_list), then ds.clean.cleanup()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71c2096f", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " ds_act = act.io.armfiles.read_netcdf(full_path)\n", + " print(type(ds_act))\n", + " ds_act.clean.cleanup()\n", + "\n", + " # or \n", + " # ds.clean.cleanup()\n", + "except Exception as e:\n", + " print(\"ERROR\", e)\n", + " ds_act = ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "844f8505", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter valid variables for ACT qc plotting\n", + "condition = (\"qc_\" + df_info.var_name).apply(lambda x: ds_act[x].attrs.get(\"flag_masks\") is not None \n", + " if x in list(ds.variables) else False)\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_3 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings))) &\n", + " condition\n", + " ]\n", + "df_filter_3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd38cc6", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "try:\n", + " qc_variable = df_filter_3.var_name.values[0]\n", + " print(qc_variable)\n", + "except Exception as e:\n", + " print(e)\n", + " \n", + "try:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + " qc_ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "d76e4d27", + "metadata": {}, + "source": [ + "#### bonus: choose variables to plot from a dropdown menu " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb733804", + "metadata": {}, + "outputs": [], + "source": [ + "# Valid variables filtering\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_4 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82d5c20d", + "metadata": {}, + "outputs": [], + "source": [ + "# example 1: using xarray plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2fc9aaa4", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# fig, ax = plt.subplots(figsize=(10, 4))\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "# fig.clear() # Remove old lines from plot and plot new one\n", + "# if len(ds[var].dims)==2:\n", + "# ds[var].plot(x=\"time\", add_colorbar=False)\n", + "# else:\n", + "# ds[var].plot()\n", + "# plt.grid()\n", + "# plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fabdd802", + "metadata": {}, + "outputs": [], + "source": [ + "# example 2: using act plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "962b4186", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "\n", + "\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "\n", + "# i_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + "# i_display.add_subplots((1,), figsize=(10, 4))\n", + "# ax = i_display.plot(var, subplot_index=(0,), set_title=f\"{var} ({ds_act[var].attrs['long_name']})\",)\n", + "\n", + "# ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "# ax.grid()\n", + "# plt.show()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/MFRSRCLDOD/mfrsrcldod1min.c1.ipynb b/VAPs/quicklook/MFRSRCLDOD/mfrsrcldod1min.c1.ipynb new file mode 100644 index 00000000..8b384458 --- /dev/null +++ b/VAPs/quicklook/MFRSRCLDOD/mfrsrcldod1min.c1.ipynb @@ -0,0 +1,339 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# MFRSRCLDOD1MIN.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/mfrsrcldod) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'mfrsrcldod1min'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2017-11-01', 'facility': 'M1', 'site': 'asi', 'start_date': '2016-05-02'}, {'end_date': '2019-10-27', 'facility': 'C1', 'site': 'ena', 'start_date': '2014-06-01'}, {'end_date': '2007-11-15', 'facility': 'M1', 'site': 'fkb', 'start_date': '2007-05-07'}, {'end_date': '2015-08-29', 'facility': 'M1', 'site': 'mao', 'start_date': '2015-04-17'}, {'end_date': '2018-03-13', 'facility': 'S1', 'site': 'mcq', 'start_date': '2016-04-01'}, {'end_date': '2019-04-30', 'facility': 'M1', 'site': 'cor', 'start_date': '2018-09-18'}, {'end_date': '2012-02-05', 'facility': 'M1', 'site': 'gan', 'start_date': '2011-10-09'}, {'end_date': '2010-12-29', 'facility': 'M1', 'site': 'grw', 'start_date': '2009-05-04'}, {'end_date': '2013-07-01', 'facility': 'M1', 'site': 'pvc', 'start_date': '2012-07-09'}, {'end_date': '2005-09-15', 'facility': 'M1', 'site': 'pye', 'start_date': '2005-02-19'}, {'end_date': '2012-04-01', 'facility': 'M1', 'site': 'pgh', 'start_date': '2011-07-19'}, {'end_date': '2021-01-29', 'facility': 'C1', 'site': 'sgp', 'start_date': '1998-01-01'}, {'end_date': '2011-10-19', 'facility': 'E10', 'site': 'sgp', 'start_date': '1997-10-31'}, {'end_date': '2021-09-21', 'facility': 'E11', 'site': 'sgp', 'start_date': '1997-08-23'}, {'end_date': '2021-09-21', 'facility': 'E12', 'site': 'sgp', 'start_date': '2001-07-24'}, {'end_date': '2022-06-30', 'facility': 'E13', 'site': 'sgp', 'start_date': '1998-07-10'}, {'end_date': '2021-09-21', 'facility': 'E15', 'site': 'sgp', 'start_date': '1997-09-10'}, {'end_date': '2011-11-15', 'facility': 'E16', 'site': 'sgp', 'start_date': '1997-08-21'}, {'end_date': '2009-11-17', 'facility': 'E18', 'site': 'sgp', 'start_date': '1997-10-17'}, {'end_date': '2011-05-23', 'facility': 'E19', 'site': 'sgp', 'start_date': '1998-07-09'}, {'end_date': '2009-10-14', 'facility': 'E1', 'site': 'sgp', 'start_date': '1997-11-21'}, {'end_date': '2011-11-17', 'facility': 'E20', 'site': 'sgp', 'start_date': '1999-04-24'}, {'end_date': '2009-12-01', 'facility': 'E22', 'site': 'sgp', 'start_date': '1999-01-15'}, {'end_date': '2009-11-14', 'facility': 'E24', 'site': 'sgp', 'start_date': '1997-11-26'}, {'end_date': '2002-04-08', 'facility': 'E25', 'site': 'sgp', 'start_date': '1998-01-11'}, {'end_date': '2009-12-04', 'facility': 'E27', 'site': 'sgp', 'start_date': '2003-12-30'}, {'end_date': '2009-10-20', 'facility': 'E2', 'site': 'sgp', 'start_date': '1997-11-05'}, {'end_date': '2021-06-22', 'facility': 'E31', 'site': 'sgp', 'start_date': '2011-11-26'}, {'end_date': '2021-09-21', 'facility': 'E32', 'site': 'sgp', 'start_date': '2011-11-26'}, {'end_date': '2021-09-21', 'facility': 'E33', 'site': 'sgp', 'start_date': '2011-09-27'}, {'end_date': '2021-09-21', 'facility': 'E34', 'site': 'sgp', 'start_date': '2011-09-28'}, {'end_date': '2021-09-21', 'facility': 'E35', 'site': 'sgp', 'start_date': '2011-09-28'}, {'end_date': '2021-09-21', 'facility': 'E36', 'site': 'sgp', 'start_date': '2011-10-18'}, {'end_date': '2021-09-21', 'facility': 'E37', 'site': 'sgp', 'start_date': '2011-11-02'}, {'end_date': '2017-10-15', 'facility': 'E38', 'site': 'sgp', 'start_date': '2011-12-15'}, {'end_date': '2009-10-28', 'facility': 'E3', 'site': 'sgp', 'start_date': '1998-07-24'}, {'end_date': '2011-09-26', 'facility': 'E4', 'site': 'sgp', 'start_date': '1997-12-20'}, {'end_date': '2009-11-02', 'facility': 'E5', 'site': 'sgp', 'start_date': '1998-03-22'}, {'end_date': '2011-10-18', 'facility': 'E6', 'site': 'sgp', 'start_date': '2003-12-19'}, {'end_date': '2011-11-14', 'facility': 'E7', 'site': 'sgp', 'start_date': '1999-07-12'}, {'end_date': '2009-11-10', 'facility': 'E8', 'site': 'sgp', 'start_date': '1997-09-03'}, {'end_date': '2021-09-21', 'facility': 'E9', 'site': 'sgp', 'start_date': '2008-03-25'}, {'end_date': '2014-06-04', 'facility': 'C1', 'site': 'twp', 'start_date': '1999-10-22'}, {'end_date': '2013-09-09', 'facility': 'C2', 'site': 'twp', 'start_date': '1999-09-08'}, {'end_date': '2014-10-05', 'facility': 'C3', 'site': 'twp', 'start_date': '2002-03-07'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2021-01-27'\n", + "date_end = '2021-01-29'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['optical_depth_instantaneous']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'optical_depth_instantaneous'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'optical_depth_instantaneous'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/MICROBASE/.ipynb_checkpoints/microbasepi2.c1-checkpoint.ipynb b/VAPs/quicklook/MICROBASE/.ipynb_checkpoints/microbasepi2.c1-checkpoint.ipynb new file mode 100644 index 00000000..72c386b4 --- /dev/null +++ b/VAPs/quicklook/MICROBASE/.ipynb_checkpoints/microbasepi2.c1-checkpoint.ipynb @@ -0,0 +1,468 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# MICROBASEPI2.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/microbase) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'microbasepi2'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2011-03-22', 'facility': 'C1', 'site': 'nsa', 'start_date': '2002-01-01'}, {'end_date': '2010-12-30', 'facility': 'C1', 'site': 'sgp', 'start_date': '1996-11-08'}, {'end_date': '2011-02-25', 'facility': 'C1', 'site': 'twp', 'start_date': '1999-07-01'}, {'end_date': '2009-02-13', 'facility': 'C2', 'site': 'twp', 'start_date': '2002-01-01'}, {'end_date': '2011-02-27', 'facility': 'C3', 'site': 'twp', 'start_date': '2005-11-04'}]" + ] + }, + { + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "ac6764f5", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The following locations and date ranges are available for this VAP:\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
sitefacilitystart_dateend_date
0nsaC12002-01-012011-03-22
1sgpC11996-11-082010-12-30
2twpC11999-07-012011-02-25
3twpC22002-01-012009-02-13
4twpC32005-11-042011-02-27
\n", + "
" + ], + "text/plain": [ + " site facility start_date end_date\n", + "0 nsa C1 2002-01-01 2011-03-22\n", + "1 sgp C1 1996-11-08 2010-12-30\n", + "2 twp C1 1999-07-01 2011-02-25\n", + "3 twp C2 2002-01-01 2009-02-13\n", + "4 twp C3 2005-11-04 2011-02-27" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2010-12-28'\n", + "date_end = '2010-12-30'" + ] + }, + { + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'/data/archive/sgp/sgpmicrobasepi2C1.c1'" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['20101228', '20101229', '20101230']" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "51feea2e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['/data/archive/sgp/sgpmicrobasepi2C1.c1/sgpmicrobasepi2C1.c1.20101228.000000.cdf',\n", + " '/data/archive/sgp/sgpmicrobasepi2C1.c1/sgpmicrobasepi2C1.c1.20101229.000000.cdf',\n", + " '/data/archive/sgp/sgpmicrobasepi2C1.c1/sgpmicrobasepi2C1.c1.20101230.000000.cdf']" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "2d9b10cd", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Requirement already satisfied: cftime in /home/kefeimo/.conda/envs/jupyter-vaps/lib/python3.8/site-packages (1.6.2)\n", + "Requirement already satisfied: numpy>1.13.3 in /home/kefeimo/.conda/envs/jupyter-vaps/lib/python3.8/site-packages (from cftime) (1.24.2)\n" + ] + } + ], + "source": [ + "! pip install cftime " + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [ + { + "ename": "ValueError", + "evalue": "Failed to decode variable 'time_offset': unable to decode time units 'seconds since base_time' with 'the default calendar'. Try opening your dataset with decode_times=False or installing cftime if it is not installed.", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/coding/times.py:184\u001b[0m, in \u001b[0;36m_decode_cf_datetime_dtype\u001b[0;34m(data, units, calendar, use_cftime)\u001b[0m\n\u001b[1;32m 183\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m--> 184\u001b[0m result \u001b[38;5;241m=\u001b[39m \u001b[43mdecode_cf_datetime\u001b[49m\u001b[43m(\u001b[49m\u001b[43mexample_value\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43munits\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mcalendar\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43muse_cftime\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 185\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mException\u001b[39;00m:\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/coding/times.py:308\u001b[0m, in \u001b[0;36mdecode_cf_datetime\u001b[0;34m(num_dates, units, calendar, use_cftime)\u001b[0m\n\u001b[1;32m 307\u001b[0m \u001b[38;5;28;01melif\u001b[39;00m use_cftime:\n\u001b[0;32m--> 308\u001b[0m dates \u001b[38;5;241m=\u001b[39m \u001b[43m_decode_datetime_with_cftime\u001b[49m\u001b[43m(\u001b[49m\u001b[43mflat_num_dates\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43munits\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mcalendar\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 309\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/coding/times.py:208\u001b[0m, in \u001b[0;36m_decode_datetime_with_cftime\u001b[0;34m(num_dates, units, calendar)\u001b[0m\n\u001b[1;32m 206\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m num_dates\u001b[38;5;241m.\u001b[39msize \u001b[38;5;241m>\u001b[39m \u001b[38;5;241m0\u001b[39m:\n\u001b[1;32m 207\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m np\u001b[38;5;241m.\u001b[39masarray(\n\u001b[0;32m--> 208\u001b[0m \u001b[43mcftime\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mnum2date\u001b[49m\u001b[43m(\u001b[49m\u001b[43mnum_dates\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43munits\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mcalendar\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43monly_use_cftime_datetimes\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43;01mTrue\u001b[39;49;00m\u001b[43m)\u001b[49m\n\u001b[1;32m 209\u001b[0m )\n\u001b[1;32m 210\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n", + "File \u001b[0;32msrc/cftime/_cftime.pyx:580\u001b[0m, in \u001b[0;36mcftime._cftime.num2date\u001b[0;34m()\u001b[0m\n", + "File \u001b[0;32msrc/cftime/_cftime.pyx:110\u001b[0m, in \u001b[0;36mcftime._cftime._dateparse\u001b[0;34m()\u001b[0m\n", + "File \u001b[0;32msrc/cftime/_cftime.pyx:767\u001b[0m, in \u001b[0;36mcftime._cftime._parse_date\u001b[0;34m()\u001b[0m\n", + "\u001b[0;31mValueError\u001b[0m: Unable to parse date string 'base_time'", + "\nDuring handling of the above exception, another exception occurred:\n", + "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/conventions.py:551\u001b[0m, in \u001b[0;36mdecode_cf_variables\u001b[0;34m(variables, attributes, concat_characters, mask_and_scale, decode_times, decode_coords, drop_variables, use_cftime, decode_timedelta)\u001b[0m\n\u001b[1;32m 550\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m--> 551\u001b[0m new_vars[k] \u001b[38;5;241m=\u001b[39m \u001b[43mdecode_cf_variable\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 552\u001b[0m \u001b[43m \u001b[49m\u001b[43mk\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 553\u001b[0m \u001b[43m \u001b[49m\u001b[43mv\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 554\u001b[0m \u001b[43m \u001b[49m\u001b[43mconcat_characters\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mconcat_characters\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 555\u001b[0m \u001b[43m \u001b[49m\u001b[43mmask_and_scale\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mmask_and_scale\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 556\u001b[0m \u001b[43m \u001b[49m\u001b[43mdecode_times\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mdecode_times\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 557\u001b[0m \u001b[43m \u001b[49m\u001b[43mstack_char_dim\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mstack_char_dim\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 558\u001b[0m \u001b[43m \u001b[49m\u001b[43muse_cftime\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43muse_cftime\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 559\u001b[0m \u001b[43m \u001b[49m\u001b[43mdecode_timedelta\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mdecode_timedelta\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 560\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 561\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mException\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m e:\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/conventions.py:397\u001b[0m, in \u001b[0;36mdecode_cf_variable\u001b[0;34m(name, var, concat_characters, mask_and_scale, decode_times, decode_endianness, stack_char_dim, use_cftime, decode_timedelta)\u001b[0m\n\u001b[1;32m 396\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m decode_times:\n\u001b[0;32m--> 397\u001b[0m var \u001b[38;5;241m=\u001b[39m \u001b[43mtimes\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mCFDatetimeCoder\u001b[49m\u001b[43m(\u001b[49m\u001b[43muse_cftime\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43muse_cftime\u001b[49m\u001b[43m)\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mdecode\u001b[49m\u001b[43m(\u001b[49m\u001b[43mvar\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mname\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mname\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 399\u001b[0m dimensions, data, attributes, encoding \u001b[38;5;241m=\u001b[39m variables\u001b[38;5;241m.\u001b[39munpack_for_decoding(var)\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/coding/times.py:716\u001b[0m, in \u001b[0;36mCFDatetimeCoder.decode\u001b[0;34m(self, variable, name)\u001b[0m\n\u001b[1;32m 715\u001b[0m calendar \u001b[38;5;241m=\u001b[39m pop_to(attrs, encoding, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mcalendar\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[0;32m--> 716\u001b[0m dtype \u001b[38;5;241m=\u001b[39m \u001b[43m_decode_cf_datetime_dtype\u001b[49m\u001b[43m(\u001b[49m\u001b[43mdata\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43munits\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mcalendar\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43muse_cftime\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 717\u001b[0m transform \u001b[38;5;241m=\u001b[39m partial(\n\u001b[1;32m 718\u001b[0m decode_cf_datetime,\n\u001b[1;32m 719\u001b[0m units\u001b[38;5;241m=\u001b[39munits,\n\u001b[1;32m 720\u001b[0m calendar\u001b[38;5;241m=\u001b[39mcalendar,\n\u001b[1;32m 721\u001b[0m use_cftime\u001b[38;5;241m=\u001b[39m\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39muse_cftime,\n\u001b[1;32m 722\u001b[0m )\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/coding/times.py:194\u001b[0m, in \u001b[0;36m_decode_cf_datetime_dtype\u001b[0;34m(data, units, calendar, use_cftime)\u001b[0m\n\u001b[1;32m 189\u001b[0m msg \u001b[38;5;241m=\u001b[39m (\n\u001b[1;32m 190\u001b[0m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124munable to decode time units \u001b[39m\u001b[38;5;132;01m{\u001b[39;00munits\u001b[38;5;132;01m!r}\u001b[39;00m\u001b[38;5;124m with \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mcalendar_msg\u001b[38;5;132;01m!r}\u001b[39;00m\u001b[38;5;124m. Try \u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 191\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mopening your dataset with decode_times=False or installing cftime \u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 192\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mif it is not installed.\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 193\u001b[0m )\n\u001b[0;32m--> 194\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(msg)\n\u001b[1;32m 195\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n", + "\u001b[0;31mValueError\u001b[0m: unable to decode time units 'seconds since base_time' with 'the default calendar'. Try opening your dataset with decode_times=False or installing cftime if it is not installed.", + "\nDuring handling of the above exception, another exception occurred:\n", + "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[0;32mIn[7], line 3\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[38;5;66;03m# Load files as a single dataset\u001b[39;00m\n\u001b[1;32m 2\u001b[0m files_list \u001b[38;5;241m=\u001b[39m files_filter \n\u001b[0;32m----> 3\u001b[0m ds \u001b[38;5;241m=\u001b[39m \u001b[43mact\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mio\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43marmfiles\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mread_netcdf\u001b[49m\u001b[43m(\u001b[49m\u001b[43mfiles_list\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 4\u001b[0m ds\u001b[38;5;241m.\u001b[39mclean\u001b[38;5;241m.\u001b[39mcleanup()\n\u001b[1;32m 5\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;132;01m{\u001b[39;00m\u001b[38;5;28mlen\u001b[39m(files_list)\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m files loaded\u001b[39m\u001b[38;5;124m'\u001b[39m)\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/act/io/armfiles.py:168\u001b[0m, in \u001b[0;36mread_netcdf\u001b[0;34m(filenames, concat_dim, return_None, combine, decode_times, use_cftime, use_base_time, combine_attrs, cleanup_qc, keep_variables, **kwargs)\u001b[0m\n\u001b[1;32m 164\u001b[0m ds \u001b[38;5;241m=\u001b[39m xr\u001b[38;5;241m.\u001b[39mopen_mfdataset(filenames, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs)\n\u001b[1;32m 166\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m 167\u001b[0m \u001b[38;5;66;03m# When all else fails raise the orginal exception\u001b[39;00m\n\u001b[0;32m--> 168\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m exception\n\u001b[1;32m 170\u001b[0m \u001b[38;5;66;03m# If requested use base_time and time_offset to derive time. Assumes that the units\u001b[39;00m\n\u001b[1;32m 171\u001b[0m \u001b[38;5;66;03m# of both are in seconds and that the value is number of seconds since epoch.\u001b[39;00m\n\u001b[1;32m 172\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m use_base_time:\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/act/io/armfiles.py:143\u001b[0m, in \u001b[0;36mread_netcdf\u001b[0;34m(filenames, concat_dim, return_None, combine, decode_times, use_cftime, use_base_time, combine_attrs, cleanup_qc, keep_variables, **kwargs)\u001b[0m\n\u001b[1;32m 139\u001b[0m except_tuple \u001b[38;5;241m=\u001b[39m except_tuple \u001b[38;5;241m+\u001b[39m (\u001b[38;5;167;01mFileNotFoundError\u001b[39;00m, \u001b[38;5;167;01mOSError\u001b[39;00m)\n\u001b[1;32m 141\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[1;32m 142\u001b[0m \u001b[38;5;66;03m# Read data file with Xarray function\u001b[39;00m\n\u001b[0;32m--> 143\u001b[0m ds \u001b[38;5;241m=\u001b[39m \u001b[43mxr\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mopen_mfdataset\u001b[49m\u001b[43m(\u001b[49m\u001b[43mfilenames\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 145\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m except_tuple \u001b[38;5;28;01mas\u001b[39;00m exception:\n\u001b[1;32m 146\u001b[0m \u001b[38;5;66;03m# If requested return None for File not found error\u001b[39;00m\n\u001b[1;32m 147\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mtype\u001b[39m(exception)\u001b[38;5;241m.\u001b[39m\u001b[38;5;18m__name__\u001b[39m \u001b[38;5;241m==\u001b[39m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mFileNotFoundError\u001b[39m\u001b[38;5;124m'\u001b[39m:\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/backends/api.py:998\u001b[0m, in \u001b[0;36mopen_mfdataset\u001b[0;34m(paths, chunks, concat_dim, compat, preprocess, engine, data_vars, coords, combine, parallel, join, attrs_file, combine_attrs, **kwargs)\u001b[0m\n\u001b[1;32m 995\u001b[0m open_ \u001b[38;5;241m=\u001b[39m open_dataset\n\u001b[1;32m 996\u001b[0m getattr_ \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mgetattr\u001b[39m\n\u001b[0;32m--> 998\u001b[0m datasets \u001b[38;5;241m=\u001b[39m [open_(p, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mopen_kwargs) \u001b[38;5;28;01mfor\u001b[39;00m p \u001b[38;5;129;01min\u001b[39;00m paths]\n\u001b[1;32m 999\u001b[0m closers \u001b[38;5;241m=\u001b[39m [getattr_(ds, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m_close\u001b[39m\u001b[38;5;124m\"\u001b[39m) \u001b[38;5;28;01mfor\u001b[39;00m ds \u001b[38;5;129;01min\u001b[39;00m datasets]\n\u001b[1;32m 1000\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m preprocess \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/backends/api.py:998\u001b[0m, in \u001b[0;36m\u001b[0;34m(.0)\u001b[0m\n\u001b[1;32m 995\u001b[0m open_ \u001b[38;5;241m=\u001b[39m open_dataset\n\u001b[1;32m 996\u001b[0m getattr_ \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mgetattr\u001b[39m\n\u001b[0;32m--> 998\u001b[0m datasets \u001b[38;5;241m=\u001b[39m [\u001b[43mopen_\u001b[49m\u001b[43m(\u001b[49m\u001b[43mp\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mopen_kwargs\u001b[49m\u001b[43m)\u001b[49m \u001b[38;5;28;01mfor\u001b[39;00m p \u001b[38;5;129;01min\u001b[39;00m paths]\n\u001b[1;32m 999\u001b[0m closers \u001b[38;5;241m=\u001b[39m [getattr_(ds, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m_close\u001b[39m\u001b[38;5;124m\"\u001b[39m) \u001b[38;5;28;01mfor\u001b[39;00m ds \u001b[38;5;129;01min\u001b[39;00m datasets]\n\u001b[1;32m 1000\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m preprocess \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/backends/api.py:541\u001b[0m, in \u001b[0;36mopen_dataset\u001b[0;34m(filename_or_obj, engine, chunks, cache, decode_cf, mask_and_scale, decode_times, decode_timedelta, use_cftime, concat_characters, decode_coords, drop_variables, inline_array, backend_kwargs, **kwargs)\u001b[0m\n\u001b[1;32m 529\u001b[0m decoders \u001b[38;5;241m=\u001b[39m _resolve_decoders_kwargs(\n\u001b[1;32m 530\u001b[0m decode_cf,\n\u001b[1;32m 531\u001b[0m open_backend_dataset_parameters\u001b[38;5;241m=\u001b[39mbackend\u001b[38;5;241m.\u001b[39mopen_dataset_parameters,\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 537\u001b[0m decode_coords\u001b[38;5;241m=\u001b[39mdecode_coords,\n\u001b[1;32m 538\u001b[0m )\n\u001b[1;32m 540\u001b[0m overwrite_encoded_chunks \u001b[38;5;241m=\u001b[39m kwargs\u001b[38;5;241m.\u001b[39mpop(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124moverwrite_encoded_chunks\u001b[39m\u001b[38;5;124m\"\u001b[39m, \u001b[38;5;28;01mNone\u001b[39;00m)\n\u001b[0;32m--> 541\u001b[0m backend_ds \u001b[38;5;241m=\u001b[39m \u001b[43mbackend\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mopen_dataset\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 542\u001b[0m \u001b[43m \u001b[49m\u001b[43mfilename_or_obj\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 543\u001b[0m \u001b[43m \u001b[49m\u001b[43mdrop_variables\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mdrop_variables\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 544\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mdecoders\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 545\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 546\u001b[0m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 547\u001b[0m ds \u001b[38;5;241m=\u001b[39m _dataset_from_backend_dataset(\n\u001b[1;32m 548\u001b[0m backend_ds,\n\u001b[1;32m 549\u001b[0m filename_or_obj,\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 557\u001b[0m \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs,\n\u001b[1;32m 558\u001b[0m )\n\u001b[1;32m 559\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m ds\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/backends/netCDF4_.py:592\u001b[0m, in \u001b[0;36mNetCDF4BackendEntrypoint.open_dataset\u001b[0;34m(self, filename_or_obj, mask_and_scale, decode_times, concat_characters, decode_coords, drop_variables, use_cftime, decode_timedelta, group, mode, format, clobber, diskless, persist, lock, autoclose)\u001b[0m\n\u001b[1;32m 590\u001b[0m store_entrypoint \u001b[38;5;241m=\u001b[39m StoreBackendEntrypoint()\n\u001b[1;32m 591\u001b[0m \u001b[38;5;28;01mwith\u001b[39;00m close_on_error(store):\n\u001b[0;32m--> 592\u001b[0m ds \u001b[38;5;241m=\u001b[39m \u001b[43mstore_entrypoint\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mopen_dataset\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 593\u001b[0m \u001b[43m \u001b[49m\u001b[43mstore\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 594\u001b[0m \u001b[43m \u001b[49m\u001b[43mmask_and_scale\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mmask_and_scale\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 595\u001b[0m \u001b[43m \u001b[49m\u001b[43mdecode_times\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mdecode_times\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 596\u001b[0m \u001b[43m \u001b[49m\u001b[43mconcat_characters\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mconcat_characters\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 597\u001b[0m \u001b[43m \u001b[49m\u001b[43mdecode_coords\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mdecode_coords\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 598\u001b[0m \u001b[43m \u001b[49m\u001b[43mdrop_variables\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mdrop_variables\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 599\u001b[0m \u001b[43m \u001b[49m\u001b[43muse_cftime\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43muse_cftime\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 600\u001b[0m \u001b[43m \u001b[49m\u001b[43mdecode_timedelta\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mdecode_timedelta\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 601\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 602\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m ds\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/backends/store.py:35\u001b[0m, in \u001b[0;36mStoreBackendEntrypoint.open_dataset\u001b[0;34m(self, store, mask_and_scale, decode_times, concat_characters, decode_coords, drop_variables, use_cftime, decode_timedelta)\u001b[0m\n\u001b[1;32m 32\u001b[0m \u001b[38;5;28mvars\u001b[39m, attrs \u001b[38;5;241m=\u001b[39m store\u001b[38;5;241m.\u001b[39mload()\n\u001b[1;32m 33\u001b[0m encoding \u001b[38;5;241m=\u001b[39m store\u001b[38;5;241m.\u001b[39mget_encoding()\n\u001b[0;32m---> 35\u001b[0m \u001b[38;5;28mvars\u001b[39m, attrs, coord_names \u001b[38;5;241m=\u001b[39m \u001b[43mconventions\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mdecode_cf_variables\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 36\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43mvars\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[1;32m 37\u001b[0m \u001b[43m \u001b[49m\u001b[43mattrs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 38\u001b[0m \u001b[43m \u001b[49m\u001b[43mmask_and_scale\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mmask_and_scale\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 39\u001b[0m \u001b[43m \u001b[49m\u001b[43mdecode_times\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mdecode_times\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 40\u001b[0m \u001b[43m \u001b[49m\u001b[43mconcat_characters\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mconcat_characters\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 41\u001b[0m \u001b[43m \u001b[49m\u001b[43mdecode_coords\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mdecode_coords\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 42\u001b[0m \u001b[43m \u001b[49m\u001b[43mdrop_variables\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mdrop_variables\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 43\u001b[0m \u001b[43m \u001b[49m\u001b[43muse_cftime\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43muse_cftime\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 44\u001b[0m \u001b[43m \u001b[49m\u001b[43mdecode_timedelta\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mdecode_timedelta\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 45\u001b[0m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 47\u001b[0m ds \u001b[38;5;241m=\u001b[39m Dataset(\u001b[38;5;28mvars\u001b[39m, attrs\u001b[38;5;241m=\u001b[39mattrs)\n\u001b[1;32m 48\u001b[0m ds \u001b[38;5;241m=\u001b[39m ds\u001b[38;5;241m.\u001b[39mset_coords(coord_names\u001b[38;5;241m.\u001b[39mintersection(\u001b[38;5;28mvars\u001b[39m))\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/conventions.py:562\u001b[0m, in \u001b[0;36mdecode_cf_variables\u001b[0;34m(variables, attributes, concat_characters, mask_and_scale, decode_times, decode_coords, drop_variables, use_cftime, decode_timedelta)\u001b[0m\n\u001b[1;32m 551\u001b[0m new_vars[k] \u001b[38;5;241m=\u001b[39m decode_cf_variable(\n\u001b[1;32m 552\u001b[0m k,\n\u001b[1;32m 553\u001b[0m v,\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 559\u001b[0m decode_timedelta\u001b[38;5;241m=\u001b[39mdecode_timedelta,\n\u001b[1;32m 560\u001b[0m )\n\u001b[1;32m 561\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mException\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m e:\n\u001b[0;32m--> 562\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;28mtype\u001b[39m(e)(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mFailed to decode variable \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mk\u001b[38;5;132;01m!r}\u001b[39;00m\u001b[38;5;124m: \u001b[39m\u001b[38;5;132;01m{\u001b[39;00me\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m\"\u001b[39m)\n\u001b[1;32m 563\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m decode_coords \u001b[38;5;129;01min\u001b[39;00m [\u001b[38;5;28;01mTrue\u001b[39;00m, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mcoordinates\u001b[39m\u001b[38;5;124m\"\u001b[39m, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mall\u001b[39m\u001b[38;5;124m\"\u001b[39m]:\n\u001b[1;32m 564\u001b[0m var_attrs \u001b[38;5;241m=\u001b[39m new_vars[k]\u001b[38;5;241m.\u001b[39mattrs\n", + "\u001b[0;31mValueError\u001b[0m: Failed to decode variable 'time_offset': unable to decode time units 'seconds since base_time' with 'the default calendar'. Try opening your dataset with decode_times=False or installing cftime if it is not installed." + ] + } + ], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['mwr_scale_factor', 'liquid_water_content', 'aqc_liquid_water_content']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'mwr_scale_factor'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/MICROBASE/.ipynb_checkpoints/microbasepiavg.c1-checkpoint.ipynb b/VAPs/quicklook/MICROBASE/.ipynb_checkpoints/microbasepiavg.c1-checkpoint.ipynb new file mode 100644 index 00000000..6f4b3ae0 --- /dev/null +++ b/VAPs/quicklook/MICROBASE/.ipynb_checkpoints/microbasepiavg.c1-checkpoint.ipynb @@ -0,0 +1,1757 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# MICROBASEPIAVG.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/microbase) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'microbasepiavg'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2011-03-22', 'facility': 'C1', 'site': 'nsa', 'start_date': '2002-01-01'}, {'end_date': '2010-12-30', 'facility': 'C1', 'site': 'sgp', 'start_date': '1996-11-08'}, {'end_date': '2011-02-25', 'facility': 'C1', 'site': 'twp', 'start_date': '1999-07-01'}, {'end_date': '2009-02-13', 'facility': 'C2', 'site': 'twp', 'start_date': '2002-01-01'}, {'end_date': '2011-02-27', 'facility': 'C3', 'site': 'twp', 'start_date': '2005-11-04'}]" + ] + }, + { + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "ac6764f5", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The following locations and date ranges are available for this VAP:\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
sitefacilitystart_dateend_date
0nsaC12002-01-012011-03-22
1sgpC11996-11-082010-12-30
2twpC11999-07-012011-02-25
3twpC22002-01-012009-02-13
4twpC32005-11-042011-02-27
\n", + "
" + ], + "text/plain": [ + " site facility start_date end_date\n", + "0 nsa C1 2002-01-01 2011-03-22\n", + "1 sgp C1 1996-11-08 2010-12-30\n", + "2 twp C1 1999-07-01 2011-02-25\n", + "3 twp C2 2002-01-01 2009-02-13\n", + "4 twp C3 2005-11-04 2011-02-27" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2010-12-28'\n", + "date_end = '2010-12-30'" + ] + }, + { + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'/data/archive/sgp/sgpmicrobasepiavgC1.c1'" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['20101228', '20101229', '20101230']" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "51feea2e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['/data/archive/sgp/sgpmicrobasepiavgC1.c1/sgpmicrobasepiavgC1.c1.20101228.001000.cdf',\n", + " '/data/archive/sgp/sgpmicrobasepiavgC1.c1/sgpmicrobasepiavgC1.c1.20101229.001000.cdf',\n", + " '/data/archive/sgp/sgpmicrobasepiavgC1.c1/sgpmicrobasepiavgC1.c1.20101230.001000.cdf']" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "3 files loaded\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
<xarray.Dataset>\n",
+       "Dimensions:                    (time: 216, nheights: 233)\n",
+       "Coordinates:\n",
+       "  * time                       (time) datetime64[ns] 2010-12-28T00:10:00 ... ...\n",
+       "Dimensions without coordinates: nheights\n",
+       "Data variables: (12/17)\n",
+       "    base_time                  (time) datetime64[ns] 2010-12-28T00:10:00 ... ...\n",
+       "    time_offset                (time) datetime64[ns] 2010-12-28T00:10:00 ... ...\n",
+       "    Heights                    (time, nheights) float32 dask.array<chunksize=(72, 233), meta=np.ndarray>\n",
+       "    Avg_Retrieved_LWC          (time, nheights) float32 dask.array<chunksize=(72, 233), meta=np.ndarray>\n",
+       "    Avg_Retrieved_IWC          (time, nheights) float32 dask.array<chunksize=(72, 233), meta=np.ndarray>\n",
+       "    Avg_LiqEffectiveRadius     (time, nheights) float32 dask.array<chunksize=(72, 233), meta=np.ndarray>\n",
+       "    ...                         ...\n",
+       "    Integrated_CloudFraction   (time) float32 dask.array<chunksize=(72,), meta=np.ndarray>\n",
+       "    aqc_CloudFraction          (time) float32 dask.array<chunksize=(72,), meta=np.ndarray>\n",
+       "    aqc_CloudMissing           (time) float32 dask.array<chunksize=(72,), meta=np.ndarray>\n",
+       "    lat                        (time) float32 36.61 36.61 36.61 ... 36.61 36.61\n",
+       "    lon                        (time) float32 -97.49 -97.49 ... -97.49 -97.49\n",
+       "    alt                        (time) float32 318.0 318.0 318.0 ... 318.0 318.0\n",
+       "Attributes: (12/13)\n",
+       "    process_version:                $State: vap-microbasepi-1.2-1.sol5_10 $\n",
+       "    command_line:                   microbasepi -d 20101228 -f sgpC1\n",
+       "    site_id:                        sgp\n",
+       "    facility_id:                    C1: Lamont, Oklahoma\n",
+       "    input_datastreams_description:  A string consisting of the datastream(s),...\n",
+       "    input_datastreams_num:          3\n",
+       "    ...                             ...\n",
+       "    history:                        created by user dsmgr on machine garnet a...\n",
+       "    _file_dates:                    ['20101228', '20101229', '20101230']\n",
+       "    _file_times:                    ['001000', '001000', '001000']\n",
+       "    datastream:                     sgpmicrobasepiavgC1.c1\n",
+       "    _datastream:                    sgpmicrobasepiavgC1.c1\n",
+       "    _arm_standards_flag:            1
" + ], + "text/plain": [ + "\n", + "Dimensions: (time: 216, nheights: 233)\n", + "Coordinates:\n", + " * time (time) datetime64[ns] 2010-12-28T00:10:00 ... ...\n", + "Dimensions without coordinates: nheights\n", + "Data variables: (12/17)\n", + " base_time (time) datetime64[ns] 2010-12-28T00:10:00 ... ...\n", + " time_offset (time) datetime64[ns] 2010-12-28T00:10:00 ... ...\n", + " Heights (time, nheights) float32 dask.array\n", + " Avg_Retrieved_LWC (time, nheights) float32 dask.array\n", + " Avg_Retrieved_IWC (time, nheights) float32 dask.array\n", + " Avg_LiqEffectiveRadius (time, nheights) float32 dask.array\n", + " ... ...\n", + " Integrated_CloudFraction (time) float32 dask.array\n", + " aqc_CloudFraction (time) float32 dask.array\n", + " aqc_CloudMissing (time) float32 dask.array\n", + " lat (time) float32 36.61 36.61 36.61 ... 36.61 36.61\n", + " lon (time) float32 -97.49 -97.49 ... -97.49 -97.49\n", + " alt (time) float32 318.0 318.0 318.0 ... 318.0 318.0\n", + "Attributes: (12/13)\n", + " process_version: $State: vap-microbasepi-1.2-1.sol5_10 $\n", + " command_line: microbasepi -d 20101228 -f sgpC1\n", + " site_id: sgp\n", + " facility_id: C1: Lamont, Oklahoma\n", + " input_datastreams_description: A string consisting of the datastream(s),...\n", + " input_datastreams_num: 3\n", + " ... ...\n", + " history: created by user dsmgr on machine garnet a...\n", + " _file_dates: ['20101228', '20101229', '20101230']\n", + " _file_times: ['001000', '001000', '001000']\n", + " datastream: sgpmicrobasepiavgC1.c1\n", + " _datastream: sgpmicrobasepiavgC1.c1\n", + " _arm_standards_flag: 1" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['avg_retrieved_lwc', 'avg_retrieved_iwc', 'avg_liq_effective_radius']\n", + "variables_to_plot = ['avg_retrieved_iwc', 'avg_liq_effective_radius']" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [ + { + "ename": "KeyError", + "evalue": "'avg_retrieved_iwc'", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mKeyError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[0;32mIn[11], line 5\u001b[0m\n\u001b[1;32m 2\u001b[0m ts_display\u001b[38;5;241m.\u001b[39madd_subplots((\u001b[38;5;28mlen\u001b[39m(variables_to_plot),), figsize \u001b[38;5;241m=\u001b[39m (\u001b[38;5;241m9.5\u001b[39m,\u001b[38;5;241m4\u001b[39m\u001b[38;5;241m*\u001b[39m\u001b[38;5;28mlen\u001b[39m(variables_to_plot)))\n\u001b[1;32m 4\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m i,v \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28menumerate\u001b[39m(variables_to_plot):\n\u001b[0;32m----> 5\u001b[0m ts_ax \u001b[38;5;241m=\u001b[39m ts_display\u001b[38;5;241m.\u001b[39mplot(v, subplot_index\u001b[38;5;241m=\u001b[39m(i,), set_title\u001b[38;5;241m=\u001b[39m\u001b[43mds\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mvariables\u001b[49m\u001b[43m[\u001b[49m\u001b[43mv\u001b[49m\u001b[43m]\u001b[49m\u001b[38;5;241m.\u001b[39mattrs[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mlong_name\u001b[39m\u001b[38;5;124m'\u001b[39m],)\n\u001b[1;32m 6\u001b[0m ts_ax\u001b[38;5;241m.\u001b[39mgrid()\n\u001b[1;32m 8\u001b[0m plt\u001b[38;5;241m.\u001b[39mshow()\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/core/utils.py:453\u001b[0m, in \u001b[0;36mFrozen.__getitem__\u001b[0;34m(self, key)\u001b[0m\n\u001b[1;32m 452\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21m__getitem__\u001b[39m(\u001b[38;5;28mself\u001b[39m, key: K) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m V:\n\u001b[0;32m--> 453\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mmapping\u001b[49m\u001b[43m[\u001b[49m\u001b[43mkey\u001b[49m\u001b[43m]\u001b[49m\n", + "\u001b[0;31mKeyError\u001b[0m: 'avg_retrieved_iwc'" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "fa59298dbac54ea38a2f036fff77b0e0", + "version_major": 2, + "version_minor": 0 + }, + "image/png": "iVBORw0KGgoAAAANSUhEUgAAA7YAAAMgCAYAAAAXxf7GAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAA9hAAAPYQGoP6dpAAA9KElEQVR4nO3df2zV9b348Vdpaatu7SLMWgS7uquTXTI22sAoaxad1oDhhmQLXVysejFZs+0S6HQDWXQQk2ZbZnadglsEzRJ0jT/jH52jWTZ+CDcZTVkWIXeLcC1sraQ1a1G3IvD5/mHod12LUqSn5w2PR3L+OO99PvR1trfdefL5HE9BlmVZAAAAQKKmTPYAAAAA8GEIWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrCdYDt27IilS5fGjBkzoqCgIF588cUPPGf79u1RU1MTpaWlcc0118Rjjz028YMCAAAkSthOsLfffjvmzp0b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UvLdePbS888/HzfffHN8/OMfj7Kysli4cGH8+te/zuG05LPx/l467ZVXXomioqL47Gc/O7EDkozx7qWhoaFYt25dVFVVRUlJSXzyk5+MLVu25Gha8t1499PWrVtj7ty5cemll0ZlZWXcdddd0d/fn6NpyVc7duyIpUuXxowZM6KgoCBefPHFDzzH++/cEbaJamtri1WrVsW6deuiq6sr6uvrY/HixSO+NuifHTp0KJYsWRL19fXR1dUV9913X6xcuTKee+65HE9OvhnvXtqxY0fcfPPN0d7eHp2dnXHDDTfE0qVLo6urK8eTk2/Gu5dOGxgYiKampvjSl76Uo0nJd+eyl5YvXx6/+c1vYvPmzfG///u/8fTTT8f111+fw6nJV+PdT7t27YqmpqZYsWJFvPrqq/HMM8/E73//+7j77rtzPDn55u233465c+fGI488clbHe/+dYxlJmj9/ftbc3Dxi7frrr8/WrFkz5vHf+c53suuvv37E2te//vXs85///ITNSBrGu5fG8ulPfzpbv379+R6NxJzrXmpsbMy+973vZQ888EA2d+7cCZyQVIx3L/3qV7/KysvLs/7+/lyMR2LGu59+9KMfZddcc82ItYcffjibOXPmhM1IeiIie+GFF973GO+/c8sV2wQdP348Ojs7o6GhYcR6Q0ND7N69e8xz9uzZM+r4W265Jfbu3RvvvvvuhM1KfjuXvfSvTp06FceOHYvLL798IkYkEee6l5544ol47bXX4oEHHpjoEUnEueyll156KWpra+OHP/xhXHXVVXHdddfFPffcE3//+99zMTJ57Fz2U11dXRw5ciTa29sjy7J444034tlnn41bb701FyNzAfH+O7eKJnsAxq+vry9OnjwZFRUVI9YrKiqit7d3zHN6e3vHPP7EiRPR19cXlZWVEzYv+etc9tK/+vGPfxxvv/12LF++fCJGJBHnspf+/Oc/x5o1a2Lnzp1RVOT/jnjPueylgwcPxq5du6K0tDReeOGF6Ovri2984xvx5ptv+pztRe5c9lNdXV1s3bo1Ghsb4x//+EecOHEi/uM//iN++tOf5mJkLiDef+eWK7YJKygoGPE8y7JRax90/FjrXHzGu5dOe/rpp+P73/9+tLW1xRVXXDFR45GQs91LJ0+ejNtuuy3Wr18f1113Xa7GIyHj+b106tSpKCgoiK1bt8b8+fNjyZIl8dBDD8WTTz7pqi0RMb79tH///li5cmXcf//90dnZGS+//HIcOnQompubczEqFxjvv3PHX5EnaPr06VFYWDjqbxqPHj066m+FTrvyyivHPL6oqCimTZs2YbOS385lL53W1tYWK1asiGeeeSZuuummiRyTBIx3Lx07diz27t0bXV1d8a1vfSsi3ouTLMuiqKgotm3bFjfeeGNOZie/nMvvpcrKyrjqqquivLx8eG327NmRZVkcOXIkrr322gmdmfx1LvuptbU1Fi1aFPfee29ERHzmM5+Jyy67LOrr6+PBBx90lY2z5v13brlim6Di4uKoqamJjo6OEesdHR1RV1c35jkLFy4cdfy2bduitrY2pk6dOmGzkt/OZS9FvHel9s4774ynnnrKZ46IiPHvpbKysvjjH/8Y+/btG340NzfHpz71qdi3b18sWLAgV6OTZ87l99KiRYvir3/9a7z11lvDa3/6059iypQpMXPmzAmdl/x2LvvpnXfeiSlTRr5FLiwsjIj/f7UNzob33zk2Sf/SKj6kX/7yl9nUqVOzzZs3Z/v3789WrVqVXXbZZdn//d//ZVmWZWvWrMluv/324eMPHjyYXXrppdnq1auz/fv3Z5s3b86mTp2aPfvss5P1EsgT491LTz31VFZUVJQ9+uijWU9Pz/Djb3/722S9BPLEePfSv/JvRea08e6lY8eOZTNnzsy+8pWvZK+++mq2ffv27Nprr83uvvvuyXoJ5JHx7qcnnngiKyoqyjZu3Ji99tpr2a5du7La2tps/vz5k/USyBPHjh3Lurq6sq6uriwisoceeijr6urKXn/99SzLvP+ebMI2YY8++mhWVVWVFRcXZ/Pmzcu2b98+/J/dcccd2Re/+MURx//ud7/LPve5z2XFxcXZJz7xiWzTpk05nph8NZ699MUvfjGLiFGPO+64I/eDk3fG+3vpnwlb/tl499KBAweym266KbvkkkuymTNnZi0tLdk777yT46nJV+PdTw8//HD26U9/OrvkkkuyysrK7Gtf+1p25MiRHE9Nvvntb3/7vu+BvP+eXAVZ5p4KAAAA0uUztgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsA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zFpaWrJ33nknx1OTr8a7nx5++OHs05/+dHbJJZdklZWV2de+9rXsyJEjOZ6afPPb3/72fd8Def89uQqyzD0VAAAApMtnbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgaf8PfYg0zZCMkMIAAAAASUVORK5CYII=", + "text/html": [ + "\n", + "
\n", + "
\n", + " Figure\n", + "
\n", + " \n", + "
\n", + " " + ], + "text/plain": [ + "Canvas(toolbar=Toolbar(toolitems=[('Home', 'Reset original view', 'home', 'home'), ('Back', 'Back to previous …" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'avg_retrieved_lwc'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/MICROBASE/MICROBASE_tutorial.ipynb b/VAPs/quicklook/MICROBASE/MICROBASE_tutorial.ipynb new file mode 100644 index 00000000..0db35219 --- /dev/null +++ b/VAPs/quicklook/MICROBASE/MICROBASE_tutorial.ipynb @@ -0,0 +1,838 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# MICROBASEPI.C1 Notebook\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/microbase) for more information about this vap." + ] + }, + { + "cell_type": "markdown", + "id": "97097763", + "metadata": {}, + "source": [ + "In this notebook, we demonstrate the workflow to explore ARM vap data (using microbasepi as an example.) Value-added products (VAPs) are higher-order data products that have been analyzed and processed to ease scientists’ use of ARM data in atmospheric research and global climate models. \n", + "Here is the main content we will cover." + ] + }, + { + "cell_type": "markdown", + "id": "eddec40f", + "metadata": {}, + "source": [ + "# Table of Content\n", + "## Access the data\n", + "* How to retrieve the data\n", + "* Data path and file name conventions\n", + "* Load data\n", + "## Explore the data\n", + "* NetCDF Data structure\n", + "* Xarray essentials\n", + "* Xarray Variable\n", + "## Plot the data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "import random\n", + "\n", + "import glob\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "2372d5be", + "metadata": {}, + "source": [ + "## Access the data" + ] + }, + { + "cell_type": "markdown", + "id": "5b0d9684", + "metadata": {}, + "source": [ + "### How to retrieve the data\n", + "We assume the path \"/data/archive\" is available where you are running this notebook. You can use `os.path.exists(\"/data/archive\")` to verify if the path exists at your machine.\n", + "\n", + "\n", + "### Data path and file name conventions\n", + "There several common terminologies regarding ARM data, for example, data-stream-name, data-level, site, facility, instrument, etc. (For more details, please see [ARM Data File Standards Version 1.2](https://www.google.com/search?q=arm+datastream+facility+cite+definition&rlz=1C1GCEJ_enUS1029US1029&ei=hb41ZICrBPukqtsPvMODIA&ved=0ahUKEwjAgeiS0aL-AhV7kmoFHbzhAAQQ4dUDCBA&uact=5&oq=arm+datastream+facility+cite+definition&gs_lcp=Cgxnd3Mtd2l6LXNlcnAQAzIFCAAQogQyBQgAEKIEMgUIABCiBDIFCAAQogQyBQgAEKIEOgoIABBHENYEELADOgoIIRCgARDDBBAKSgQIQRgAUJ4DWIoOYO0PaAFwAXgAgAGMAYgBuQmSAQMyLjmYAQCgAQHIAQjAAQE&sclient=gws-wiz-serp).) \n", + "\n", + "For example, this notebook is called `microbasepi.c1`, where `microbasepi` is the \"datastream name\", and the `{process.ds_class_level}}` is the \"data level\".\n", + "\n", + "This datastream also contains site `nsa` and facility `C1`. (Note: individual datastream might have multiple site-facility pairs.)\n", + "In such a case, the data of this data-stream is stored at `/data/archive/nsa/nsamicrobasepiC1.c1`, which is in the format of `//.`. We can use the following method to assign the data-stream directory `datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )`\n", + "\n", + "The data files under datastream_dir also follows naming conventions. But once reach the datastream_dir level, the most import file naming convention to differentiate the files is \"yyyyMMdd.hhmmss\", which comes handy to filter out files based on datetime. For example, we can use `glob.glob(f'{datastream_dir}/*.200709*.*')` to filter files in 2007 September.\n", + "\n", + "(Note: refer to the https://adc.arm.gov/discovery/#/ and https://adc.arm.gov/solr8/metadata/select API to explore ARM datastream and assoicated available sites and facilities\n", + "\n", + "Please see the following examples in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d7e9eb85", + "metadata": {}, + "outputs": [], + "source": [ + "# Verify if DATA_DIR path exists\n", + "DATA_DIR = \"/data/archive\"\n", + "os.path.exists(DATA_DIR)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "586993fd", + "metadata": {}, + "outputs": [], + "source": [ + "# Speicify datastream_dir following the path conventions and check its existence\n", + "DATASTREAM_NAME = \"microbasepi\"\n", + "DATA_LEVEL = \"c1\"\n", + "site = \"nsa\"\n", + "facility = \"C1\"\n", + "datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )\n", + "print(datastream_dir)\n", + "print(os.path.exists(datastream_dir))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0742f7c1", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: list 5 (random) files under datastream_dir\n", + "files = os.listdir(datastream_dir)\n", + "files[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39b98a36", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: get most recent file\n", + "list_of_files = glob.glob(f\"{datastream_dir}/*\") # * means all if need specific format then *.csv\n", + "latest_file = max(list_of_files, key=os.path.getctime)\n", + "latest_file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "902d514e", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus sort datastream files based on datetime\n", + "files = os.listdir(datastream_dir)\n", + "file_sorted = files.copy()\n", + "file_sorted.sort() \n", + "print(file_sorted[:5])\n", + "\n", + "# to reverse\n", + "file_sorted_reverse = files.copy() \n", + "file_sorted_reverse.sort(reverse=True)\n", + "print(file_sorted_reverse[:5])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec5923b2", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: pattern matching\n", + "# filter the 200709** files under datastream_dir\n", + "files_filter = glob.glob(f'{datastream_dir}/*.200709*.*')\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c4a4aa18", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: use armnotebook_utils.file_filter (TODO) to filter files based on datastream info \n" + ] + }, + { + "cell_type": "markdown", + "id": "cfeb9efc", + "metadata": {}, + "source": [ + "### Load data\n", + "The arm data is stored in [NetCDF](https://en.wikipedia.org/wiki/NetCDF#:~:text=%22NetCDF%20(network%20Common%20Data%20Form,format%20for%20representing%20scientific%20data.) format. We can use xarray's `open_dataset` method to load single file, or `open_mfdataset` to open multiple files. (Note: the latter will still return a single xarray dataset object by combining multiple files.)\n", + "\n", + "Note: open_dataset keeps the file handle open and lazy loads its contents. All parameters are passed directly to open_dataset. It is a preferable method over load_dataset for memory-effiency. (more details, please see [xarray.open_dataset](https://docs.xarray.dev/en/stable/generated/xarray.open_dataset.html))\n", + "\n", + "See the following example in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a440a329", + "metadata": {}, + "outputs": [], + "source": [ + "# open single file\n", + "full_path = latest_file\n", + "print(full_path)\n", + "ds_single = xr.open_dataset(full_path)\n", + "ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0143a3d", + "metadata": {}, + "outputs": [], + "source": [ + "print(ds_single)\n", + "print(type(ds_single))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c0f8939", + "metadata": {}, + "outputs": [], + "source": [ + "# open multiple files\n", + "n_files = 3\n", + "full_paths = [os.path.join(datastream_dir, f_path) for f_path in file_sorted_reverse[:n_files]]\n", + "print(full_paths)\n", + "try: # Note: sometimes multiple files cannot be merged, so we used try except here.\n", + " ds_mutiple = xr.open_mfdataset(full_paths)\n", + " # ds_mutiple\n", + " print(type(ds_mutiple))\n", + " print(ds_mutiple)\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "3fa59943", + "metadata": {}, + "source": [ + "## Explore the data" + ] + }, + { + "cell_type": "markdown", + "id": "571f69b9", + "metadata": {}, + "source": [ + "### NetCDF Data structure \n", + "\n", + "\n", + "(If you are confident with NetCDF basics and xarray essentials, feel free to skip this session.)\n", + "Before we dive into data exploration, there are some eseential concpets we should be familiar with (shown below): \n", + "* Dataset\n", + "* Data array\n", + "* Variable\n", + "* Dimenssion\n", + "* Coordinate\n", + "* Data Type\n", + "* Meta Data (Attributes)\n", + "\n", + "We will not go into details about NetCDF basics and here are some references you might find helpful\n", + "* [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#)\n", + "* [Network Common Data Form (NetCDF)](https://www.unidata.ucar.edu/software/netcdf/) and [A Brief History of (netCDF) Time](https://www.unidata.ucar.edu/software/netcdf/time/recs.html)\n", + "\n", + "\n", + "\n", + "### Xarray essentials\n", + "Earlier we introduced the xarray pacakge and used `open_dataset` and `open_mfdataset` to retrieve NetCDF data file as an xarray Dataset object. Recall that you can review the data in a notebook by using `print(ds)` or `ds`. \n", + "\n", + "Using xarray to retrieve the aforementioned NetCDF basics is straightforwared. In general,\n", + "* Dataset: `ds`\n", + "* Data array: `ds.variables`\n", + "* Variable: `ds.ds.variables`\n", + "* Dimenssion: `ds.dims`\n", + "* Coordinate: `ds.coords`\n", + "* Data Type: `type`\n", + "* Meta Data (Attributes)\n", + "\n", + "Also, here are some references if you are new to xarray\n", + "* [Xarray in 45 minutes](https://tutorial.xarray.dev/overview/xarray-in-45-min.html)\n", + "* [Handling NetCDF Files using XArray for Absolute Beginners](https://towardsdatascience.com/handling-netcdf-files-using-xarray-for-absolute-beginners-111a8ab4463f)\n", + "\n", + "Try the following commands in action\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b3eac323", + "metadata": {}, + "outputs": [], + "source": [ + "ds = ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77ecf85d", + "metadata": {}, + "outputs": [], + "source": [ + "# Dataset \n", + "ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25e7de09", + "metadata": {}, + "outputs": [], + "source": [ + "# Data array, Variable\n", + "# Note: the info can be overwhelming. Do not attempt to grasp everything at the first glance.\n", + "ds.variables" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c41b67e", + "metadata": {}, + "outputs": [], + "source": [ + "# dimenssions\n", + "ds.dims" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "156f1dfc", + "metadata": {}, + "outputs": [], + "source": [ + "# coordinates\n", + "ds.coords" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "277d6064", + "metadata": {}, + "outputs": [], + "source": [ + "# Meta Data (Attributes)\n", + "ds.attrs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d334681f", + "metadata": {}, + "outputs": [], + "source": [ + "# type\n", + "print(type(ds))\n", + "print(type(ds.variables))\n", + "print(type(ds.dims))\n", + "print(type(ds.coords))\n", + "print(type(ds.attrs))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "643399d6", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: retrieve variable names only\n", + "list(ds.variables)" + ] + }, + { + "cell_type": "markdown", + "id": "1cf36878", + "metadata": {}, + "source": [ + "#### Discussion: variable vs. coordinates vs. dimenssions. \n", + "From [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#):\n", + "\n", + "* Dimession: A dimension may be used to represent a real physical dimension, for example, time, latitude, longitude, or height. A dimension might also be used to index other quantities, for example station or model-run-number.\n", + "A netCDF dimension has both a name and a length. A dimension length is an arbitrary positive integer, except that one dimension in a netCDF dataset can have the length UNLIMITED.\n", + "\n", + "* Variables: Variables are used to store the bulk of the data in a netCDF dataset. A variable represents an array of values of the same type. A scalar value is treated as a 0-dimensional array. A variable has a name, a data type, and a shape described by its list of dimensions specified when the variable is created. A variable may also have associated attributes, which may be added, deleted or changed after the variable is created.\n", + "\n", + "* Coordinate (Variables): It is legal for a variable to have the same name as a dimension. Such variables have no special meaning to the netCDF library. However there is a convention that such variables should be treated in a special way by software using this library.\n", + "A variable with the same name as a dimension is called a coordinate variable. It typically defines a physical coordinate corresponding to that dimension.\n", + "\n", + "\n", + "Tips: By definition, coordinates and dimenssions are also variable. For example ds.time is a coordinate but it is also a special variable. But in practice, when we talk about variable, it implies regular/non-coordinate variable. We will use this convention for the remaining notebook. " + ] + }, + { + "cell_type": "markdown", + "id": "d47120f9", + "metadata": {}, + "source": [ + "### Xarray Variable\n", + "\n", + "\n", + "We can use `ds.variables` to access varialbes of a dataset. But many times viewing print-out the whole datasets can be overwhemling. Instead, we would work on the individual variable (also called Xarray data array.) using the `ds[var_name]` syntax, where `var_name` is the name of the variable. See the following examples in action.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5a57e136", + "metadata": {}, + "outputs": [], + "source": [ + "print(type(ds[\"time\"]))\n", + "ds[\"time\"]" + ] + }, + { + "cell_type": "markdown", + "id": "51ec643e", + "metadata": {}, + "source": [ + "#### Variable properties\n", + "For individual varible, the following properties are mostly used with the assocaited xarray method to retrieve them. (assuming var = ds[var_name])\n", + "* name: `var.name`\n", + "* data content: `var.data`\n", + "* attributes: `var.attrs`\n", + "* dimenstions: `var.dims`\n", + "* data type: `var.data.dtype`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a632a525", + "metadata": {}, + "outputs": [], + "source": [ + "var_name = \"time\"\n", + "var = ds[var_name]\n", + "\n", + "print(\"var.name: \\n\", var.name, \"\\n\")\n", + "print(\"var.data: \\n\", var.data, \"\\n\")\n", + "print(\"var.attrs: \\n\", var.attrs, \"\\n\")\n", + "print(\"var.dims: \\n\", var.dims, \"\\n\")\n", + "print(\"var.data.dtype: \\n\", var.data.dtype, \"\\n\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b256b07f", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: indivdual variable info table (collect and display variable info as a tabular format)\n", + "df_info = pd.DataFrame()\n", + "df_info[\"var_name\"] = list(ds.variables)\n", + "df_info[\"dims\"] = df_info.var_name.apply(lambda x: ds[x].dims)\n", + "df_info[\"is_dim\"] = df_info.var_name.apply(lambda x: x in ds.dims)\n", + "df_info[\"n_dim\"] = df_info.var_name.apply(lambda x: len(ds[x].dims))\n", + "df_info[\"attrs\"] = df_info.var_name.apply(lambda x: ds[x].attrs)\n", + "df_info[\"dtype\"] = df_info.var_name.apply(lambda x: ds[x].data.dtype)\n", + "df_info" + ] + }, + { + "cell_type": "markdown", + "id": "e6a36b96", + "metadata": {}, + "source": [ + "### Data cleaning/Preprocessing (skipped)\n", + "Data cleaning and preprocessing is an important stage in the the data analysis pipeline. However it will be out of the scope of this notebook. For more information about data cleaning and preprocessing basics, here are some references. \n", + "* [Xarray Fundamentals](https://earth-env-data-science.github.io/lectures/xarray/xarray.html)\n", + "* [Xarray Tutorial — Pangeo Gallery documentation](http://gallery.pangeo.io/repos/pangeo-data/pangeo-tutorial-gallery/xarray.html)\n", + "* [Pythonic Data Cleaning With pandas and NumPy](https://realpython.com/python-data-cleaning-numpy-pandas/)\n", + "* [Pandas - Cleaning Data](https://www.w3schools.com/python/pandas/pandas_cleaning.asp)" + ] + }, + { + "cell_type": "markdown", + "id": "ae6e1a69", + "metadata": {}, + "source": [ + "### Plotting\n", + "Data visualization (or plotting) is another important data analysis topic and deserves its own discussion. Here in this notebook we will only demonstrate basic tool for simple data visualization tasks.\n", + "\n", + "Here are some reference you might find useful:\n", + "* xarray plotting: https://docs.xarray.dev/en/stable/user-guide/plotting.html\n", + "* Atmospheric Community Toolkit (ACT): https://arm-doe.github.io/ACT/index.html\n", + "\n", + "Note: this notebook is auto-generated using a template. It uses a general idea to select variable(s) to plot and is not customized to each indivdual notebook. Feel free to change the variables in intrrests, especially certain figure is failed to plot." + ] + }, + { + "cell_type": "markdown", + "id": "7e417fb2", + "metadata": {}, + "source": [ + "#### 1-dimenssional basic time series plot\n", + "\n", + "For the following plot we would like to find variables such that\n", + "* it has one and only one dimession\n", + "* \"time\" is its coordinate variable\n", + "* it is not a dimenssion itself,\n", + "* it is not a special variable with substrings within [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "\n", + "Please see the following example in action. (For more details about pandas filtering, please see the following references.)\n", + "* [pandas: multiple conditions while indexing data frame](https://stackoverflow.com/questions/22591174/pandas-multiple-conditions-while-indexing-data-frame-unexpected-behavior)\n", + "* [pandas.DataFrame.apply](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.apply.html)\n", + "* [How to test if a string contains one of the substrings in a list...](https://stackoverflow.com/questions/26577516/how-to-test-if-a-string-contains-one-of-the-substrings-in-a-list-in-pandas" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30edac2d", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==1, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter = df_info[(df_info.n_dim == 1) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4777a659", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_1d = df_filter.var_name.values[0]\n", + " var_1d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb5ae985", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " ds[var_1d].plot()\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "9e41066e", + "metadata": {}, + "source": [ + "#### 2-dimenssional basic plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09bd4adc", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==2, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_2 = df_info[(df_info.n_dim == 2) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2cdea1a9", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_2d = df_filter_2.var_name.values[0]\n", + " var_2d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5f48879", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " print(ds[var_2d].dims)\n", + " # ds[var_2d].plot()\n", + "\n", + " # conventionally, use \"time\" as x-axis\n", + " ds[var_2d].plot(x=\"time\")\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "7554b2a6", + "metadata": {}, + "source": [ + "#### qc-plotting (optional)\n", + "\n", + "Note: act qc plotting has more strict requirement, one of them is the associated qc variable need to have \"flag_masks\" attributes. Which is added by using ds = act.io.armfiles.read_netcdf(files_list), then ds.clean.cleanup()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71c2096f", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " ds_act = act.io.armfiles.read_netcdf(full_path)\n", + " print(type(ds_act))\n", + " ds_act.clean.cleanup()\n", + "\n", + " # or \n", + " # ds.clean.cleanup()\n", + "except Exception as e:\n", + " print(\"ERROR\", e)\n", + " ds_act = ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "844f8505", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter valid variables for ACT qc plotting\n", + "condition = (\"qc_\" + df_info.var_name).apply(lambda x: ds_act[x].attrs.get(\"flag_masks\") is not None \n", + " if x in list(ds.variables) else False)\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_3 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings))) &\n", + " condition\n", + " ]\n", + "df_filter_3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd38cc6", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "try:\n", + " qc_variable = df_filter_3.var_name.values[0]\n", + " print(qc_variable)\n", + "except Exception as e:\n", + " print(e)\n", + " \n", + "try:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + " qc_ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "d76e4d27", + "metadata": {}, + "source": [ + "#### bonus: choose variables to plot from a dropdown menu " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb733804", + "metadata": {}, + "outputs": [], + "source": [ + "# Valid variables filtering\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_4 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82d5c20d", + "metadata": {}, + "outputs": [], + "source": [ + "# example 1: using xarray plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2fc9aaa4", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# fig, ax = plt.subplots(figsize=(10, 4))\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "# fig.clear() # Remove old lines from plot and plot new one\n", + "# if len(ds[var].dims)==2:\n", + "# ds[var].plot(x=\"time\", add_colorbar=False)\n", + "# else:\n", + "# ds[var].plot()\n", + "# plt.grid()\n", + "# plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fabdd802", + "metadata": {}, + "outputs": [], + "source": [ + "# example 2: using act plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "962b4186", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "\n", + "\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "\n", + "# i_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + "# i_display.add_subplots((1,), figsize=(10, 4))\n", + "# ax = i_display.plot(var, subplot_index=(0,), set_title=f\"{var} ({ds_act[var].attrs['long_name']})\",)\n", + "\n", + "# ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "# ax.grid()\n", + "# plt.show()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/MICROBASE/microbasepi.c1.ipynb b/VAPs/quicklook/MICROBASE/microbasepi.c1.ipynb new file mode 100644 index 00000000..a88ae73e --- /dev/null +++ b/VAPs/quicklook/MICROBASE/microbasepi.c1.ipynb @@ -0,0 +1,297 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# MICROBASEPI.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/microbase) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'microbasepi'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2011-03-22', 'facility': 'C1', 'site': 'nsa', 'start_date': '2009-01-01'}, {'end_date': '2010-12-30', 'facility': 'C1', 'site': 'sgp', 'start_date': '2010-01-01'}, {'end_date': '2011-02-25', 'facility': 'C1', 'site': 'twp', 'start_date': '2005-03-01'}, {'end_date': '2009-02-13', 'facility': 'C2', 'site': 'twp', 'start_date': '2007-01-01'}, {'end_date': '2011-02-27', 'facility': 'C3', 'site': 'twp', 'start_date': '2010-01-01'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2010-12-28'\n", + "date_end = '2010-12-30'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['MwrScaleFactor', 'Retrieved_LWC', 'Retrieved_IWC']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'MwrScaleFactor'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/MICROBASE/microbasepi2.c1.ipynb b/VAPs/quicklook/MICROBASE/microbasepi2.c1.ipynb new file mode 100644 index 00000000..f4a219e1 --- /dev/null +++ b/VAPs/quicklook/MICROBASE/microbasepi2.c1.ipynb @@ -0,0 +1,297 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# MICROBASEPI2.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/microbase) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'microbasepi2'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2011-03-22', 'facility': 'C1', 'site': 'nsa', 'start_date': '2002-01-01'}, {'end_date': '2010-12-30', 'facility': 'C1', 'site': 'sgp', 'start_date': '1996-11-08'}, {'end_date': '2011-02-25', 'facility': 'C1', 'site': 'twp', 'start_date': '1999-07-01'}, {'end_date': '2009-02-13', 'facility': 'C2', 'site': 'twp', 'start_date': '2002-01-01'}, {'end_date': '2011-02-27', 'facility': 'C3', 'site': 'twp', 'start_date': '2005-11-04'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2010-12-28'\n", + "date_end = '2010-12-30'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['mwr_scale_factor', 'liquid_water_content', 'aqc_liquid_water_content']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'mwr_scale_factor'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/MICROBASE/microbasepiavg.c1.ipynb b/VAPs/quicklook/MICROBASE/microbasepiavg.c1.ipynb new file mode 100644 index 00000000..2f07f5fb --- /dev/null +++ b/VAPs/quicklook/MICROBASE/microbasepiavg.c1.ipynb @@ -0,0 +1,297 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# MICROBASEPIAVG.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/microbase) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'microbasepiavg'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2011-03-22', 'facility': 'C1', 'site': 'nsa', 'start_date': '2002-01-01'}, {'end_date': '2010-12-30', 'facility': 'C1', 'site': 'sgp', 'start_date': '1996-11-08'}, {'end_date': '2011-02-25', 'facility': 'C1', 'site': 'twp', 'start_date': '1999-07-01'}, {'end_date': '2009-02-13', 'facility': 'C2', 'site': 'twp', 'start_date': '2002-01-01'}, {'end_date': '2011-02-27', 'facility': 'C3', 'site': 'twp', 'start_date': '2005-11-04'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2010-12-28'\n", + "date_end = '2010-12-30'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['avg_retrieved_lwc', 'avg_retrieved_iwc', 'avg_liq_effective_radius']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'avg_retrieved_lwc'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/MPLAVG/MPLAVG_tutorial.ipynb b/VAPs/quicklook/MPLAVG/MPLAVG_tutorial.ipynb new file mode 100644 index 00000000..eaaa4b29 --- /dev/null +++ b/VAPs/quicklook/MPLAVG/MPLAVG_tutorial.ipynb @@ -0,0 +1,838 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# MPLPOLAVG.C1 Notebook\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/mplavg) for more information about this vap." + ] + }, + { + "cell_type": "markdown", + "id": "97097763", + "metadata": {}, + "source": [ + "In this notebook, we demonstrate the workflow to explore ARM vap data (using mplpolavg as an example.) Value-added products (VAPs) are higher-order data products that have been analyzed and processed to ease scientists’ use of ARM data in atmospheric research and global climate models. \n", + "Here is the main content we will cover." + ] + }, + { + "cell_type": "markdown", + "id": "eddec40f", + "metadata": {}, + "source": [ + "# Table of Content\n", + "## Access the data\n", + "* How to retrieve the data\n", + "* Data path and file name conventions\n", + "* Load data\n", + "## Explore the data\n", + "* NetCDF Data structure\n", + "* Xarray essentials\n", + "* Xarray Variable\n", + "## Plot the data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "import random\n", + "\n", + "import glob\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "2372d5be", + "metadata": {}, + "source": [ + "## Access the data" + ] + }, + { + "cell_type": "markdown", + "id": "5b0d9684", + "metadata": {}, + "source": [ + "### How to retrieve the data\n", + "We assume the path \"/data/archive\" is available where you are running this notebook. You can use `os.path.exists(\"/data/archive\")` to verify if the path exists at your machine.\n", + "\n", + "\n", + "### Data path and file name conventions\n", + "There several common terminologies regarding ARM data, for example, data-stream-name, data-level, site, facility, instrument, etc. (For more details, please see [ARM Data File Standards Version 1.2](https://www.google.com/search?q=arm+datastream+facility+cite+definition&rlz=1C1GCEJ_enUS1029US1029&ei=hb41ZICrBPukqtsPvMODIA&ved=0ahUKEwjAgeiS0aL-AhV7kmoFHbzhAAQQ4dUDCBA&uact=5&oq=arm+datastream+facility+cite+definition&gs_lcp=Cgxnd3Mtd2l6LXNlcnAQAzIFCAAQogQyBQgAEKIEMgUIABCiBDIFCAAQogQyBQgAEKIEOgoIABBHENYEELADOgoIIRCgARDDBBAKSgQIQRgAUJ4DWIoOYO0PaAFwAXgAgAGMAYgBuQmSAQMyLjmYAQCgAQHIAQjAAQE&sclient=gws-wiz-serp).) \n", + "\n", + "For example, this notebook is called `mplpolavg.c1`, where `mplpolavg` is the \"datastream name\", and the `{process.ds_class_level}}` is the \"data level\".\n", + "\n", + "This datastream also contains site `fkb` and facility `M1`. (Note: individual datastream might have multiple site-facility pairs.)\n", + "In such a case, the data of this data-stream is stored at `/data/archive/fkb/fkbmplpolavgM1.c1`, which is in the format of `//.`. We can use the following method to assign the data-stream directory `datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )`\n", + "\n", + "The data files under datastream_dir also follows naming conventions. But once reach the datastream_dir level, the most import file naming convention to differentiate the files is \"yyyyMMdd.hhmmss\", which comes handy to filter out files based on datetime. For example, we can use `glob.glob(f'{datastream_dir}/*.200709*.*')` to filter files in 2007 September.\n", + "\n", + "(Note: refer to the https://adc.arm.gov/discovery/#/ and https://adc.arm.gov/solr8/metadata/select API to explore ARM datastream and assoicated available sites and facilities\n", + "\n", + "Please see the following examples in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d7e9eb85", + "metadata": {}, + "outputs": [], + "source": [ + "# Verify if DATA_DIR path exists\n", + "DATA_DIR = \"/data/archive\"\n", + "os.path.exists(DATA_DIR)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "586993fd", + "metadata": {}, + "outputs": [], + "source": [ + "# Speicify datastream_dir following the path conventions and check its existence\n", + "DATASTREAM_NAME = \"mplpolavg\"\n", + "DATA_LEVEL = \"c1\"\n", + "site = \"fkb\"\n", + "facility = \"M1\"\n", + "datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )\n", + "print(datastream_dir)\n", + "print(os.path.exists(datastream_dir))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0742f7c1", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: list 5 (random) files under datastream_dir\n", + "files = os.listdir(datastream_dir)\n", + "files[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39b98a36", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: get most recent file\n", + "list_of_files = glob.glob(f\"{datastream_dir}/*\") # * means all if need specific format then *.csv\n", + "latest_file = max(list_of_files, key=os.path.getctime)\n", + "latest_file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "902d514e", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus sort datastream files based on datetime\n", + "files = os.listdir(datastream_dir)\n", + "file_sorted = files.copy()\n", + "file_sorted.sort() \n", + "print(file_sorted[:5])\n", + "\n", + "# to reverse\n", + "file_sorted_reverse = files.copy() \n", + "file_sorted_reverse.sort(reverse=True)\n", + "print(file_sorted_reverse[:5])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec5923b2", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: pattern matching\n", + "# filter the 200709** files under datastream_dir\n", + "files_filter = glob.glob(f'{datastream_dir}/*.200709*.*')\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c4a4aa18", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: use armnotebook_utils.file_filter (TODO) to filter files based on datastream info \n" + ] + }, + { + "cell_type": "markdown", + "id": "cfeb9efc", + "metadata": {}, + "source": [ + "### Load data\n", + "The arm data is stored in [NetCDF](https://en.wikipedia.org/wiki/NetCDF#:~:text=%22NetCDF%20(network%20Common%20Data%20Form,format%20for%20representing%20scientific%20data.) format. We can use xarray's `open_dataset` method to load single file, or `open_mfdataset` to open multiple files. (Note: the latter will still return a single xarray dataset object by combining multiple files.)\n", + "\n", + "Note: open_dataset keeps the file handle open and lazy loads its contents. All parameters are passed directly to open_dataset. It is a preferable method over load_dataset for memory-effiency. (more details, please see [xarray.open_dataset](https://docs.xarray.dev/en/stable/generated/xarray.open_dataset.html))\n", + "\n", + "See the following example in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a440a329", + "metadata": {}, + "outputs": [], + "source": [ + "# open single file\n", + "full_path = latest_file\n", + "print(full_path)\n", + "ds_single = xr.open_dataset(full_path)\n", + "ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0143a3d", + "metadata": {}, + "outputs": [], + "source": [ + "print(ds_single)\n", + "print(type(ds_single))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c0f8939", + "metadata": {}, + "outputs": [], + "source": [ + "# open multiple files\n", + "n_files = 3\n", + "full_paths = [os.path.join(datastream_dir, f_path) for f_path in file_sorted_reverse[:n_files]]\n", + "print(full_paths)\n", + "try: # Note: sometimes multiple files cannot be merged, so we used try except here.\n", + " ds_mutiple = xr.open_mfdataset(full_paths)\n", + " # ds_mutiple\n", + " print(type(ds_mutiple))\n", + " print(ds_mutiple)\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "3fa59943", + "metadata": {}, + "source": [ + "## Explore the data" + ] + }, + { + "cell_type": "markdown", + "id": "571f69b9", + "metadata": {}, + "source": [ + "### NetCDF Data structure \n", + "\n", + "\n", + "(If you are confident with NetCDF basics and xarray essentials, feel free to skip this session.)\n", + "Before we dive into data exploration, there are some eseential concpets we should be familiar with (shown below): \n", + "* Dataset\n", + "* Data array\n", + "* Variable\n", + "* Dimenssion\n", + "* Coordinate\n", + "* Data Type\n", + "* Meta Data (Attributes)\n", + "\n", + "We will not go into details about NetCDF basics and here are some references you might find helpful\n", + "* [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#)\n", + "* [Network Common Data Form (NetCDF)](https://www.unidata.ucar.edu/software/netcdf/) and [A Brief History of (netCDF) Time](https://www.unidata.ucar.edu/software/netcdf/time/recs.html)\n", + "\n", + "\n", + "\n", + "### Xarray essentials\n", + "Earlier we introduced the xarray pacakge and used `open_dataset` and `open_mfdataset` to retrieve NetCDF data file as an xarray Dataset object. Recall that you can review the data in a notebook by using `print(ds)` or `ds`. \n", + "\n", + "Using xarray to retrieve the aforementioned NetCDF basics is straightforwared. In general,\n", + "* Dataset: `ds`\n", + "* Data array: `ds.variables`\n", + "* Variable: `ds.ds.variables`\n", + "* Dimenssion: `ds.dims`\n", + "* Coordinate: `ds.coords`\n", + "* Data Type: `type`\n", + "* Meta Data (Attributes)\n", + "\n", + "Also, here are some references if you are new to xarray\n", + "* [Xarray in 45 minutes](https://tutorial.xarray.dev/overview/xarray-in-45-min.html)\n", + "* [Handling NetCDF Files using XArray for Absolute Beginners](https://towardsdatascience.com/handling-netcdf-files-using-xarray-for-absolute-beginners-111a8ab4463f)\n", + "\n", + "Try the following commands in action\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b3eac323", + "metadata": {}, + "outputs": [], + "source": [ + "ds = ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77ecf85d", + "metadata": {}, + "outputs": [], + "source": [ + "# Dataset \n", + "ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25e7de09", + "metadata": {}, + "outputs": [], + "source": [ + "# Data array, Variable\n", + "# Note: the info can be overwhelming. Do not attempt to grasp everything at the first glance.\n", + "ds.variables" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c41b67e", + "metadata": {}, + "outputs": [], + "source": [ + "# dimenssions\n", + "ds.dims" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "156f1dfc", + "metadata": {}, + "outputs": [], + "source": [ + "# coordinates\n", + "ds.coords" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "277d6064", + "metadata": {}, + "outputs": [], + "source": [ + "# Meta Data (Attributes)\n", + "ds.attrs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d334681f", + "metadata": {}, + "outputs": [], + "source": [ + "# type\n", + "print(type(ds))\n", + "print(type(ds.variables))\n", + "print(type(ds.dims))\n", + "print(type(ds.coords))\n", + "print(type(ds.attrs))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "643399d6", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: retrieve variable names only\n", + "list(ds.variables)" + ] + }, + { + "cell_type": "markdown", + "id": "1cf36878", + "metadata": {}, + "source": [ + "#### Discussion: variable vs. coordinates vs. dimenssions. \n", + "From [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#):\n", + "\n", + "* Dimession: A dimension may be used to represent a real physical dimension, for example, time, latitude, longitude, or height. A dimension might also be used to index other quantities, for example station or model-run-number.\n", + "A netCDF dimension has both a name and a length. A dimension length is an arbitrary positive integer, except that one dimension in a netCDF dataset can have the length UNLIMITED.\n", + "\n", + "* Variables: Variables are used to store the bulk of the data in a netCDF dataset. A variable represents an array of values of the same type. A scalar value is treated as a 0-dimensional array. A variable has a name, a data type, and a shape described by its list of dimensions specified when the variable is created. A variable may also have associated attributes, which may be added, deleted or changed after the variable is created.\n", + "\n", + "* Coordinate (Variables): It is legal for a variable to have the same name as a dimension. Such variables have no special meaning to the netCDF library. However there is a convention that such variables should be treated in a special way by software using this library.\n", + "A variable with the same name as a dimension is called a coordinate variable. It typically defines a physical coordinate corresponding to that dimension.\n", + "\n", + "\n", + "Tips: By definition, coordinates and dimenssions are also variable. For example ds.time is a coordinate but it is also a special variable. But in practice, when we talk about variable, it implies regular/non-coordinate variable. We will use this convention for the remaining notebook. " + ] + }, + { + "cell_type": "markdown", + "id": "d47120f9", + "metadata": {}, + "source": [ + "### Xarray Variable\n", + "\n", + "\n", + "We can use `ds.variables` to access varialbes of a dataset. But many times viewing print-out the whole datasets can be overwhemling. Instead, we would work on the individual variable (also called Xarray data array.) using the `ds[var_name]` syntax, where `var_name` is the name of the variable. See the following examples in action.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5a57e136", + "metadata": {}, + "outputs": [], + "source": [ + "print(type(ds[\"time\"]))\n", + "ds[\"time\"]" + ] + }, + { + "cell_type": "markdown", + "id": "51ec643e", + "metadata": {}, + "source": [ + "#### Variable properties\n", + "For individual varible, the following properties are mostly used with the assocaited xarray method to retrieve them. (assuming var = ds[var_name])\n", + "* name: `var.name`\n", + "* data content: `var.data`\n", + "* attributes: `var.attrs`\n", + "* dimenstions: `var.dims`\n", + "* data type: `var.data.dtype`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a632a525", + "metadata": {}, + "outputs": [], + "source": [ + "var_name = \"time\"\n", + "var = ds[var_name]\n", + "\n", + "print(\"var.name: \\n\", var.name, \"\\n\")\n", + "print(\"var.data: \\n\", var.data, \"\\n\")\n", + "print(\"var.attrs: \\n\", var.attrs, \"\\n\")\n", + "print(\"var.dims: \\n\", var.dims, \"\\n\")\n", + "print(\"var.data.dtype: \\n\", var.data.dtype, \"\\n\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b256b07f", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: indivdual variable info table (collect and display variable info as a tabular format)\n", + "df_info = pd.DataFrame()\n", + "df_info[\"var_name\"] = list(ds.variables)\n", + "df_info[\"dims\"] = df_info.var_name.apply(lambda x: ds[x].dims)\n", + "df_info[\"is_dim\"] = df_info.var_name.apply(lambda x: x in ds.dims)\n", + "df_info[\"n_dim\"] = df_info.var_name.apply(lambda x: len(ds[x].dims))\n", + "df_info[\"attrs\"] = df_info.var_name.apply(lambda x: ds[x].attrs)\n", + "df_info[\"dtype\"] = df_info.var_name.apply(lambda x: ds[x].data.dtype)\n", + "df_info" + ] + }, + { + "cell_type": "markdown", + "id": "e6a36b96", + "metadata": {}, + "source": [ + "### Data cleaning/Preprocessing (skipped)\n", + "Data cleaning and preprocessing is an important stage in the the data analysis pipeline. However it will be out of the scope of this notebook. For more information about data cleaning and preprocessing basics, here are some references. \n", + "* [Xarray Fundamentals](https://earth-env-data-science.github.io/lectures/xarray/xarray.html)\n", + "* [Xarray Tutorial — Pangeo Gallery documentation](http://gallery.pangeo.io/repos/pangeo-data/pangeo-tutorial-gallery/xarray.html)\n", + "* [Pythonic Data Cleaning With pandas and NumPy](https://realpython.com/python-data-cleaning-numpy-pandas/)\n", + "* [Pandas - Cleaning Data](https://www.w3schools.com/python/pandas/pandas_cleaning.asp)" + ] + }, + { + "cell_type": "markdown", + "id": "ae6e1a69", + "metadata": {}, + "source": [ + "### Plotting\n", + "Data visualization (or plotting) is another important data analysis topic and deserves its own discussion. Here in this notebook we will only demonstrate basic tool for simple data visualization tasks.\n", + "\n", + "Here are some reference you might find useful:\n", + "* xarray plotting: https://docs.xarray.dev/en/stable/user-guide/plotting.html\n", + "* Atmospheric Community Toolkit (ACT): https://arm-doe.github.io/ACT/index.html\n", + "\n", + "Note: this notebook is auto-generated using a template. It uses a general idea to select variable(s) to plot and is not customized to each indivdual notebook. Feel free to change the variables in intrrests, especially certain figure is failed to plot." + ] + }, + { + "cell_type": "markdown", + "id": "7e417fb2", + "metadata": {}, + "source": [ + "#### 1-dimenssional basic time series plot\n", + "\n", + "For the following plot we would like to find variables such that\n", + "* it has one and only one dimession\n", + "* \"time\" is its coordinate variable\n", + "* it is not a dimenssion itself,\n", + "* it is not a special variable with substrings within [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "\n", + "Please see the following example in action. (For more details about pandas filtering, please see the following references.)\n", + "* [pandas: multiple conditions while indexing data frame](https://stackoverflow.com/questions/22591174/pandas-multiple-conditions-while-indexing-data-frame-unexpected-behavior)\n", + "* [pandas.DataFrame.apply](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.apply.html)\n", + "* [How to test if a string contains one of the substrings in a list...](https://stackoverflow.com/questions/26577516/how-to-test-if-a-string-contains-one-of-the-substrings-in-a-list-in-pandas" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30edac2d", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==1, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter = df_info[(df_info.n_dim == 1) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4777a659", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_1d = df_filter.var_name.values[0]\n", + " var_1d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb5ae985", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " ds[var_1d].plot()\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "9e41066e", + "metadata": {}, + "source": [ + "#### 2-dimenssional basic plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09bd4adc", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==2, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_2 = df_info[(df_info.n_dim == 2) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2cdea1a9", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_2d = df_filter_2.var_name.values[0]\n", + " var_2d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5f48879", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " print(ds[var_2d].dims)\n", + " # ds[var_2d].plot()\n", + "\n", + " # conventionally, use \"time\" as x-axis\n", + " ds[var_2d].plot(x=\"time\")\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "7554b2a6", + "metadata": {}, + "source": [ + "#### qc-plotting (optional)\n", + "\n", + "Note: act qc plotting has more strict requirement, one of them is the associated qc variable need to have \"flag_masks\" attributes. Which is added by using ds = act.io.armfiles.read_netcdf(files_list), then ds.clean.cleanup()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71c2096f", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " ds_act = act.io.armfiles.read_netcdf(full_path)\n", + " print(type(ds_act))\n", + " ds_act.clean.cleanup()\n", + "\n", + " # or \n", + " # ds.clean.cleanup()\n", + "except Exception as e:\n", + " print(\"ERROR\", e)\n", + " ds_act = ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "844f8505", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter valid variables for ACT qc plotting\n", + "condition = (\"qc_\" + df_info.var_name).apply(lambda x: ds_act[x].attrs.get(\"flag_masks\") is not None \n", + " if x in list(ds.variables) else False)\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_3 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings))) &\n", + " condition\n", + " ]\n", + "df_filter_3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd38cc6", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "try:\n", + " qc_variable = df_filter_3.var_name.values[0]\n", + " print(qc_variable)\n", + "except Exception as e:\n", + " print(e)\n", + " \n", + "try:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + " qc_ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "d76e4d27", + "metadata": {}, + "source": [ + "#### bonus: choose variables to plot from a dropdown menu " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb733804", + "metadata": {}, + "outputs": [], + "source": [ + "# Valid variables filtering\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_4 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82d5c20d", + "metadata": {}, + "outputs": [], + "source": [ + "# example 1: using xarray plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2fc9aaa4", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# fig, ax = plt.subplots(figsize=(10, 4))\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "# fig.clear() # Remove old lines from plot and plot new one\n", + "# if len(ds[var].dims)==2:\n", + "# ds[var].plot(x=\"time\", add_colorbar=False)\n", + "# else:\n", + "# ds[var].plot()\n", + "# plt.grid()\n", + "# plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fabdd802", + "metadata": {}, + "outputs": [], + "source": [ + "# example 2: using act plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "962b4186", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "\n", + "\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "\n", + "# i_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + "# i_display.add_subplots((1,), figsize=(10, 4))\n", + "# ax = i_display.plot(var, subplot_index=(0,), set_title=f\"{var} ({ds_act[var].attrs['long_name']})\",)\n", + "\n", + "# ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "# ax.grid()\n", + "# plt.show()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/MPLAVG/mplpolavg.c1.ipynb b/VAPs/quicklook/MPLAVG/mplpolavg.c1.ipynb new file mode 100644 index 00000000..60b74172 --- /dev/null +++ b/VAPs/quicklook/MPLAVG/mplpolavg.c1.ipynb @@ -0,0 +1,339 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# MPLPOLAVG.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/mplavg) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'mplpolavg'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2007-12-31', 'facility': 'M1', 'site': 'fkb', 'start_date': '2007-03-17'}, {'end_date': '2008-12-28', 'facility': 'M1', 'site': 'hfe', 'start_date': '2008-05-14'}, {'end_date': '2011-01-05', 'facility': 'M1', 'site': 'grw', 'start_date': '2009-04-11'}, {'end_date': '2010-07-06', 'facility': 'C1', 'site': 'nsa', 'start_date': '2006-09-28'}, {'end_date': '2010-05-31', 'facility': 'C1', 'site': 'sgp', 'start_date': '2006-06-23'}, {'end_date': '2010-11-03', 'facility': 'C1', 'site': 'twp', 'start_date': '2007-02-01'}, {'end_date': '2009-02-13', 'facility': 'C2', 'site': 'twp', 'start_date': '2006-11-27'}, {'end_date': '2011-08-17', 'facility': 'C3', 'site': 'twp', 'start_date': '2006-08-31'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2010-05-29'\n", + "date_end = '2010-05-31'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['preliminary_cbh_cross_pol', 'preliminary_cbh_co_pol']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'range_offset'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'preliminary_cbh_cross_pol'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/MPLCMASK/30smplcmask1zwang.c1.ipynb b/VAPs/quicklook/MPLCMASK/30smplcmask1zwang.c1.ipynb new file mode 100644 index 00000000..fc3c4094 --- /dev/null +++ b/VAPs/quicklook/MPLCMASK/30smplcmask1zwang.c1.ipynb @@ -0,0 +1,368 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# 30SMPLCMASK1ZWANG.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/mplcmask) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = '30smplcmask1zwang'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2019-05-01', 'facility': 'M1', 'site': 'cor', 'start_date': '2018-09-24'}, {'end_date': '2017-10-31', 'facility': 'M1', 'site': 'asi', 'start_date': '2016-07-06'}, {'end_date': '2017-01-03', 'facility': 'M1', 'site': 'awr', 'start_date': '2015-12-01'}, {'end_date': '2016-01-18', 'facility': 'S1', 'site': 'awr', 'start_date': '2015-12-06'}, {'end_date': '2015-02-12', 'facility': 'M1', 'site': 'acx', 'start_date': '2015-01-09'}, {'end_date': '2020-06-01', 'facility': 'M1', 'site': 'anx', 'start_date': '2020-02-11'}, {'end_date': '2020-06-01', 'facility': 'S2', 'site': 'anx', 'start_date': '2020-01-03'}, {'end_date': '2008-12-28', 'facility': 'M1', 'site': 'hfe', 'start_date': '2008-05-15'}, {'end_date': '2023-12-05', 'facility': 'M1', 'site': 'epc', 'start_date': '2023-01-15'}, {'end_date': '2007-12-31', 'facility': 'M1', 'site': 'fkb', 'start_date': '2007-03-17'}, {'end_date': '2018-03-24', 'facility': 'M1', 'site': 'mar', 'start_date': '2017-10-19'}, {'end_date': '2022-09-30', 'facility': 'M1', 'site': 'hou', 'start_date': '2021-08-12'}, {'end_date': '2023-12-16', 'facility': 'C1', 'site': 'ena', 'start_date': '2013-10-03'}, {'end_date': '2013-08-12', 'facility': 'M1', 'site': 'mag', 'start_date': '2012-12-13'}, {'end_date': '2015-12-01', 'facility': 'M1', 'site': 'mao', 'start_date': '2014-01-03'}, {'end_date': '2012-02-09', 'facility': 'M1', 'site': 'gan', 'start_date': '2011-09-30'}, {'end_date': '2011-01-05', 'facility': 'M1', 'site': 'grw', 'start_date': '2009-04-11'}, {'end_date': '2023-06-15', 'facility': 'M1', 'site': 'guc', 'start_date': '2021-09-01'}, {'end_date': '2020-09-04', 'facility': 'M1', 'site': 'mos', 'start_date': '2019-10-11'}, {'end_date': '2013-07-02', 'facility': 'M1', 'site': 'pvc', 'start_date': '2012-06-24'}, {'end_date': '2021-06-14', 'facility': 'M1', 'site': 'oli', 'start_date': '2013-09-12'}, {'end_date': '2023-12-16', 'facility': 'C1', 'site': 'nsa', 'start_date': '1998-05-22'}, {'end_date': '2023-12-16', 'facility': 'C1', 'site': 'sgp', 'start_date': '2010-06-01'}, {'end_date': '2014-09-13', 'facility': 'M1', 'site': 'tmp', 'start_date': '2014-01-16'}, {'end_date': '2014-07-07', 'facility': 'C1', 'site': 'twp', 'start_date': '1999-08-11'}, {'end_date': '2009-02-13', 'facility': 'C2', 'site': 'twp', 'start_date': '1998-11-19'}, {'end_date': '2015-01-06', 'facility': 'C3', 'site': 'twp', 'start_date': '2002-08-08'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2023-12-14'\n", + "date_end = '2023-12-16'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['cloud_base', 'cloud_top', 'cloud_mask']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'linear_depol_ratio'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'cloud_base'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + }, + { + "cell_type": "markdown", + "id": "706c3051", + "metadata": {}, + "source": [ + "## Backscatter Plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5f84df8e", + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "backscatter_var = 'backscatter'\n", + "backscatter_fill_val = -9999.0\n", + "\n", + "# apply log function to backscatter variable\n", + "if not 'log' in ds[backscatter_var].attrs['units']:\n", + " ds = act.corrections.ceil.correct_ceil(ds, fill_value=backscatter_fill_val, var_name=backscatter_var)\n", + "\n", + "backscatter_display = act.plotting.TimeSeriesDisplay(ds, subplot_shape=(1,), figsize=(9.5, 5))\n", + "backscatter_ax = backscatter_display.plot(backscatter_var, subplot_index=(0,), set_title=ds.variables[backscatter_var].attrs['long_name'])\n", + "\n", + "plt.show()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/MPLCMASK/MPLCMASK_tutorial.ipynb b/VAPs/quicklook/MPLCMASK/MPLCMASK_tutorial.ipynb new file mode 100644 index 00000000..acaf16c9 --- /dev/null +++ b/VAPs/quicklook/MPLCMASK/MPLCMASK_tutorial.ipynb @@ -0,0 +1,867 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# 30SMPLCMASK1ZWANG.C1 Notebook\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/mplcmask) for more information about this vap." + ] + }, + { + "cell_type": "markdown", + "id": "97097763", + "metadata": {}, + "source": [ + "In this notebook, we demonstrate the workflow to explore ARM vap data (using 30smplcmask1zwang as an example.) Value-added products (VAPs) are higher-order data products that have been analyzed and processed to ease scientists’ use of ARM data in atmospheric research and global climate models. \n", + "Here is the main content we will cover." + ] + }, + { + "cell_type": "markdown", + "id": "eddec40f", + "metadata": {}, + "source": [ + "# Table of Content\n", + "## Access the data\n", + "* How to retrieve the data\n", + "* Data path and file name conventions\n", + "* Load data\n", + "## Explore the data\n", + "* NetCDF Data structure\n", + "* Xarray essentials\n", + "* Xarray Variable\n", + "## Plot the data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "import random\n", + "\n", + "import glob\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "2372d5be", + "metadata": {}, + "source": [ + "## Access the data" + ] + }, + { + "cell_type": "markdown", + "id": "5b0d9684", + "metadata": {}, + "source": [ + "### How to retrieve the data\n", + "We assume the path \"/data/archive\" is available where you are running this notebook. You can use `os.path.exists(\"/data/archive\")` to verify if the path exists at your machine.\n", + "\n", + "\n", + "### Data path and file name conventions\n", + "There several common terminologies regarding ARM data, for example, data-stream-name, data-level, site, facility, instrument, etc. (For more details, please see [ARM Data File Standards Version 1.2](https://www.google.com/search?q=arm+datastream+facility+cite+definition&rlz=1C1GCEJ_enUS1029US1029&ei=hb41ZICrBPukqtsPvMODIA&ved=0ahUKEwjAgeiS0aL-AhV7kmoFHbzhAAQQ4dUDCBA&uact=5&oq=arm+datastream+facility+cite+definition&gs_lcp=Cgxnd3Mtd2l6LXNlcnAQAzIFCAAQogQyBQgAEKIEMgUIABCiBDIFCAAQogQyBQgAEKIEOgoIABBHENYEELADOgoIIRCgARDDBBAKSgQIQRgAUJ4DWIoOYO0PaAFwAXgAgAGMAYgBuQmSAQMyLjmYAQCgAQHIAQjAAQE&sclient=gws-wiz-serp).) \n", + "\n", + "For example, this notebook is called `30smplcmask1zwang.c1`, where `30smplcmask1zwang` is the \"datastream name\", and the `{process.ds_class_level}}` is the \"data level\".\n", + "\n", + "This datastream also contains site `cor` and facility `M1`. (Note: individual datastream might have multiple site-facility pairs.)\n", + "In such a case, the data of this data-stream is stored at `/data/archive/cor/cor30smplcmask1zwangM1.c1`, which is in the format of `//.`. We can use the following method to assign the data-stream directory `datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )`\n", + "\n", + "The data files under datastream_dir also follows naming conventions. But once reach the datastream_dir level, the most import file naming convention to differentiate the files is \"yyyyMMdd.hhmmss\", which comes handy to filter out files based on datetime. For example, we can use `glob.glob(f'{datastream_dir}/*.200709*.*')` to filter files in 2007 September.\n", + "\n", + "(Note: refer to the https://adc.arm.gov/discovery/#/ and https://adc.arm.gov/solr8/metadata/select API to explore ARM datastream and assoicated available sites and facilities\n", + "\n", + "Please see the following examples in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d7e9eb85", + "metadata": {}, + "outputs": [], + "source": [ + "# Verify if DATA_DIR path exists\n", + "DATA_DIR = \"/data/archive\"\n", + "os.path.exists(DATA_DIR)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "586993fd", + "metadata": {}, + "outputs": [], + "source": [ + "# Speicify datastream_dir following the path conventions and check its existence\n", + "DATASTREAM_NAME = \"30smplcmask1zwang\"\n", + "DATA_LEVEL = \"c1\"\n", + "site = \"cor\"\n", + "facility = \"M1\"\n", + "datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )\n", + "print(datastream_dir)\n", + "print(os.path.exists(datastream_dir))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0742f7c1", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: list 5 (random) files under datastream_dir\n", + "files = os.listdir(datastream_dir)\n", + "files[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39b98a36", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: get most recent file\n", + "list_of_files = glob.glob(f\"{datastream_dir}/*\") # * means all if need specific format then *.csv\n", + "latest_file = max(list_of_files, key=os.path.getctime)\n", + "latest_file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "902d514e", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus sort datastream files based on datetime\n", + "files = os.listdir(datastream_dir)\n", + "file_sorted = files.copy()\n", + "file_sorted.sort() \n", + "print(file_sorted[:5])\n", + "\n", + "# to reverse\n", + "file_sorted_reverse = files.copy() \n", + "file_sorted_reverse.sort(reverse=True)\n", + "print(file_sorted_reverse[:5])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec5923b2", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: pattern matching\n", + "# filter the 200709** files under datastream_dir\n", + "files_filter = glob.glob(f'{datastream_dir}/*.200709*.*')\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c4a4aa18", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: use armnotebook_utils.file_filter (TODO) to filter files based on datastream info \n" + ] + }, + { + "cell_type": "markdown", + "id": "cfeb9efc", + "metadata": {}, + "source": [ + "### Load data\n", + "The arm data is stored in [NetCDF](https://en.wikipedia.org/wiki/NetCDF#:~:text=%22NetCDF%20(network%20Common%20Data%20Form,format%20for%20representing%20scientific%20data.) format. We can use xarray's `open_dataset` method to load single file, or `open_mfdataset` to open multiple files. (Note: the latter will still return a single xarray dataset object by combining multiple files.)\n", + "\n", + "Note: open_dataset keeps the file handle open and lazy loads its contents. All parameters are passed directly to open_dataset. It is a preferable method over load_dataset for memory-effiency. (more details, please see [xarray.open_dataset](https://docs.xarray.dev/en/stable/generated/xarray.open_dataset.html))\n", + "\n", + "See the following example in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a440a329", + "metadata": {}, + "outputs": [], + "source": [ + "# open single file\n", + "full_path = latest_file\n", + "print(full_path)\n", + "ds_single = xr.open_dataset(full_path)\n", + "ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0143a3d", + "metadata": {}, + "outputs": [], + "source": [ + "print(ds_single)\n", + "print(type(ds_single))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c0f8939", + "metadata": {}, + "outputs": [], + "source": [ + "# open multiple files\n", + "n_files = 3\n", + "full_paths = [os.path.join(datastream_dir, f_path) for f_path in file_sorted_reverse[:n_files]]\n", + "print(full_paths)\n", + "try: # Note: sometimes multiple files cannot be merged, so we used try except here.\n", + " ds_mutiple = xr.open_mfdataset(full_paths)\n", + " # ds_mutiple\n", + " print(type(ds_mutiple))\n", + " print(ds_mutiple)\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "3fa59943", + "metadata": {}, + "source": [ + "## Explore the data" + ] + }, + { + "cell_type": "markdown", + "id": "571f69b9", + "metadata": {}, + "source": [ + "### NetCDF Data structure \n", + "\n", + "\n", + "(If you are confident with NetCDF basics and xarray essentials, feel free to skip this session.)\n", + "Before we dive into data exploration, there are some eseential concpets we should be familiar with (shown below): \n", + "* Dataset\n", + "* Data array\n", + "* Variable\n", + "* Dimenssion\n", + "* Coordinate\n", + "* Data Type\n", + "* Meta Data (Attributes)\n", + "\n", + "We will not go into details about NetCDF basics and here are some references you might find helpful\n", + "* [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#)\n", + "* [Network Common Data Form (NetCDF)](https://www.unidata.ucar.edu/software/netcdf/) and [A Brief History of (netCDF) Time](https://www.unidata.ucar.edu/software/netcdf/time/recs.html)\n", + "\n", + "\n", + "\n", + "### Xarray essentials\n", + "Earlier we introduced the xarray pacakge and used `open_dataset` and `open_mfdataset` to retrieve NetCDF data file as an xarray Dataset object. Recall that you can review the data in a notebook by using `print(ds)` or `ds`. \n", + "\n", + "Using xarray to retrieve the aforementioned NetCDF basics is straightforwared. In general,\n", + "* Dataset: `ds`\n", + "* Data array: `ds.variables`\n", + "* Variable: `ds.ds.variables`\n", + "* Dimenssion: `ds.dims`\n", + "* Coordinate: `ds.coords`\n", + "* Data Type: `type`\n", + "* Meta Data (Attributes)\n", + "\n", + "Also, here are some references if you are new to xarray\n", + "* [Xarray in 45 minutes](https://tutorial.xarray.dev/overview/xarray-in-45-min.html)\n", + "* [Handling NetCDF Files using XArray for Absolute Beginners](https://towardsdatascience.com/handling-netcdf-files-using-xarray-for-absolute-beginners-111a8ab4463f)\n", + "\n", + "Try the following commands in action\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b3eac323", + "metadata": {}, + "outputs": [], + "source": [ + "ds = ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77ecf85d", + "metadata": {}, + "outputs": [], + "source": [ + "# Dataset \n", + "ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25e7de09", + "metadata": {}, + "outputs": [], + "source": [ + "# Data array, Variable\n", + "# Note: the info can be overwhelming. Do not attempt to grasp everything at the first glance.\n", + "ds.variables" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c41b67e", + "metadata": {}, + "outputs": [], + "source": [ + "# dimenssions\n", + "ds.dims" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "156f1dfc", + "metadata": {}, + "outputs": [], + "source": [ + "# coordinates\n", + "ds.coords" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "277d6064", + "metadata": {}, + "outputs": [], + "source": [ + "# Meta Data (Attributes)\n", + "ds.attrs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d334681f", + "metadata": {}, + "outputs": [], + "source": [ + "# type\n", + "print(type(ds))\n", + "print(type(ds.variables))\n", + "print(type(ds.dims))\n", + "print(type(ds.coords))\n", + "print(type(ds.attrs))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "643399d6", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: retrieve variable names only\n", + "list(ds.variables)" + ] + }, + { + "cell_type": "markdown", + "id": "1cf36878", + "metadata": {}, + "source": [ + "#### Discussion: variable vs. coordinates vs. dimenssions. \n", + "From [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#):\n", + "\n", + "* Dimession: A dimension may be used to represent a real physical dimension, for example, time, latitude, longitude, or height. A dimension might also be used to index other quantities, for example station or model-run-number.\n", + "A netCDF dimension has both a name and a length. A dimension length is an arbitrary positive integer, except that one dimension in a netCDF dataset can have the length UNLIMITED.\n", + "\n", + "* Variables: Variables are used to store the bulk of the data in a netCDF dataset. A variable represents an array of values of the same type. A scalar value is treated as a 0-dimensional array. A variable has a name, a data type, and a shape described by its list of dimensions specified when the variable is created. A variable may also have associated attributes, which may be added, deleted or changed after the variable is created.\n", + "\n", + "* Coordinate (Variables): It is legal for a variable to have the same name as a dimension. Such variables have no special meaning to the netCDF library. However there is a convention that such variables should be treated in a special way by software using this library.\n", + "A variable with the same name as a dimension is called a coordinate variable. It typically defines a physical coordinate corresponding to that dimension.\n", + "\n", + "\n", + "Tips: By definition, coordinates and dimenssions are also variable. For example ds.time is a coordinate but it is also a special variable. But in practice, when we talk about variable, it implies regular/non-coordinate variable. We will use this convention for the remaining notebook. " + ] + }, + { + "cell_type": "markdown", + "id": "d47120f9", + "metadata": {}, + "source": [ + "### Xarray Variable\n", + "\n", + "\n", + "We can use `ds.variables` to access varialbes of a dataset. But many times viewing print-out the whole datasets can be overwhemling. Instead, we would work on the individual variable (also called Xarray data array.) using the `ds[var_name]` syntax, where `var_name` is the name of the variable. See the following examples in action.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5a57e136", + "metadata": {}, + "outputs": [], + "source": [ + "print(type(ds[\"time\"]))\n", + "ds[\"time\"]" + ] + }, + { + "cell_type": "markdown", + "id": "51ec643e", + "metadata": {}, + "source": [ + "#### Variable properties\n", + "For individual varible, the following properties are mostly used with the assocaited xarray method to retrieve them. (assuming var = ds[var_name])\n", + "* name: `var.name`\n", + "* data content: `var.data`\n", + "* attributes: `var.attrs`\n", + "* dimenstions: `var.dims`\n", + "* data type: `var.data.dtype`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a632a525", + "metadata": {}, + "outputs": [], + "source": [ + "var_name = \"time\"\n", + "var = ds[var_name]\n", + "\n", + "print(\"var.name: \\n\", var.name, \"\\n\")\n", + "print(\"var.data: \\n\", var.data, \"\\n\")\n", + "print(\"var.attrs: \\n\", var.attrs, \"\\n\")\n", + "print(\"var.dims: \\n\", var.dims, \"\\n\")\n", + "print(\"var.data.dtype: \\n\", var.data.dtype, \"\\n\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b256b07f", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: indivdual variable info table (collect and display variable info as a tabular format)\n", + "df_info = pd.DataFrame()\n", + "df_info[\"var_name\"] = list(ds.variables)\n", + "df_info[\"dims\"] = df_info.var_name.apply(lambda x: ds[x].dims)\n", + "df_info[\"is_dim\"] = df_info.var_name.apply(lambda x: x in ds.dims)\n", + "df_info[\"n_dim\"] = df_info.var_name.apply(lambda x: len(ds[x].dims))\n", + "df_info[\"attrs\"] = df_info.var_name.apply(lambda x: ds[x].attrs)\n", + "df_info[\"dtype\"] = df_info.var_name.apply(lambda x: ds[x].data.dtype)\n", + "df_info" + ] + }, + { + "cell_type": "markdown", + "id": "e6a36b96", + "metadata": {}, + "source": [ + "### Data cleaning/Preprocessing (skipped)\n", + "Data cleaning and preprocessing is an important stage in the the data analysis pipeline. However it will be out of the scope of this notebook. For more information about data cleaning and preprocessing basics, here are some references. \n", + "* [Xarray Fundamentals](https://earth-env-data-science.github.io/lectures/xarray/xarray.html)\n", + "* [Xarray Tutorial — Pangeo Gallery documentation](http://gallery.pangeo.io/repos/pangeo-data/pangeo-tutorial-gallery/xarray.html)\n", + "* [Pythonic Data Cleaning With pandas and NumPy](https://realpython.com/python-data-cleaning-numpy-pandas/)\n", + "* [Pandas - Cleaning Data](https://www.w3schools.com/python/pandas/pandas_cleaning.asp)" + ] + }, + { + "cell_type": "markdown", + "id": "ae6e1a69", + "metadata": {}, + "source": [ + "### Plotting\n", + "Data visualization (or plotting) is another important data analysis topic and deserves its own discussion. Here in this notebook we will only demonstrate basic tool for simple data visualization tasks.\n", + "\n", + "Here are some reference you might find useful:\n", + "* xarray plotting: https://docs.xarray.dev/en/stable/user-guide/plotting.html\n", + "* Atmospheric Community Toolkit (ACT): https://arm-doe.github.io/ACT/index.html\n", + "\n", + "Note: this notebook is auto-generated using a template. It uses a general idea to select variable(s) to plot and is not customized to each indivdual notebook. Feel free to change the variables in intrrests, especially certain figure is failed to plot." + ] + }, + { + "cell_type": "markdown", + "id": "7e417fb2", + "metadata": {}, + "source": [ + "#### 1-dimenssional basic time series plot\n", + "\n", + "For the following plot we would like to find variables such that\n", + "* it has one and only one dimession\n", + "* \"time\" is its coordinate variable\n", + "* it is not a dimenssion itself,\n", + "* it is not a special variable with substrings within [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "\n", + "Please see the following example in action. (For more details about pandas filtering, please see the following references.)\n", + "* [pandas: multiple conditions while indexing data frame](https://stackoverflow.com/questions/22591174/pandas-multiple-conditions-while-indexing-data-frame-unexpected-behavior)\n", + "* [pandas.DataFrame.apply](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.apply.html)\n", + "* [How to test if a string contains one of the substrings in a list...](https://stackoverflow.com/questions/26577516/how-to-test-if-a-string-contains-one-of-the-substrings-in-a-list-in-pandas" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30edac2d", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==1, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter = df_info[(df_info.n_dim == 1) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4777a659", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_1d = df_filter.var_name.values[0]\n", + " var_1d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb5ae985", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " ds[var_1d].plot()\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "9e41066e", + "metadata": {}, + "source": [ + "#### 2-dimenssional basic plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09bd4adc", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==2, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_2 = df_info[(df_info.n_dim == 2) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2cdea1a9", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_2d = df_filter_2.var_name.values[0]\n", + " var_2d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5f48879", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " print(ds[var_2d].dims)\n", + " # ds[var_2d].plot()\n", + "\n", + " # conventionally, use \"time\" as x-axis\n", + " ds[var_2d].plot(x=\"time\")\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "7554b2a6", + "metadata": {}, + "source": [ + "#### qc-plotting (optional)\n", + "\n", + "Note: act qc plotting has more strict requirement, one of them is the associated qc variable need to have \"flag_masks\" attributes. Which is added by using ds = act.io.armfiles.read_netcdf(files_list), then ds.clean.cleanup()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71c2096f", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " ds_act = act.io.armfiles.read_netcdf(full_path)\n", + " print(type(ds_act))\n", + " ds_act.clean.cleanup()\n", + "\n", + " # or \n", + " # ds.clean.cleanup()\n", + "except Exception as e:\n", + " print(\"ERROR\", e)\n", + " ds_act = ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "844f8505", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter valid variables for ACT qc plotting\n", + "condition = (\"qc_\" + df_info.var_name).apply(lambda x: ds_act[x].attrs.get(\"flag_masks\") is not None \n", + " if x in list(ds.variables) else False)\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_3 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings))) &\n", + " condition\n", + " ]\n", + "df_filter_3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd38cc6", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "try:\n", + " qc_variable = df_filter_3.var_name.values[0]\n", + " print(qc_variable)\n", + "except Exception as e:\n", + " print(e)\n", + " \n", + "try:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + " qc_ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "d76e4d27", + "metadata": {}, + "source": [ + "#### bonus: choose variables to plot from a dropdown menu " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb733804", + "metadata": {}, + "outputs": [], + "source": [ + "# Valid variables filtering\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_4 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82d5c20d", + "metadata": {}, + "outputs": [], + "source": [ + "# example 1: using xarray plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2fc9aaa4", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# fig, ax = plt.subplots(figsize=(10, 4))\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "# fig.clear() # Remove old lines from plot and plot new one\n", + "# if len(ds[var].dims)==2:\n", + "# ds[var].plot(x=\"time\", add_colorbar=False)\n", + "# else:\n", + "# ds[var].plot()\n", + "# plt.grid()\n", + "# plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fabdd802", + "metadata": {}, + "outputs": [], + "source": [ + "# example 2: using act plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "962b4186", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "\n", + "\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "\n", + "# i_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + "# i_display.add_subplots((1,), figsize=(10, 4))\n", + "# ax = i_display.plot(var, subplot_index=(0,), set_title=f\"{var} ({ds_act[var].attrs['long_name']})\",)\n", + "\n", + "# ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "# ax.grid()\n", + "# plt.show()\n" + ] + }, + { + "cell_type": "markdown", + "id": "2e2746a4", + "metadata": {}, + "source": [ + "## Backscatter Plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1eaa2114", + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "backscatter_var = 'backscatter'\n", + "backscatter_fill_val = -9999.0\n", + "\n", + "# apply log function to backscatter variable\n", + "if not 'log' in ds[backscatter_var].attrs['units']:\n", + " ds = act.corrections.ceil.correct_ceil(ds, fill_value=backscatter_fill_val, var_name=backscatter_var)\n", + "\n", + "backscatter_display = act.plotting.TimeSeriesDisplay(ds, subplot_shape=(1,), figsize=(9.5, 5))\n", + "backscatter_ax = backscatter_display.plot(backscatter_var, subplot_index=(0,), set_title=ds.variables[backscatter_var].attrs['long_name'])\n", + "\n", + "plt.show()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/MPLCMASKML/MPLCMASKML_tutorial.ipynb b/VAPs/quicklook/MPLCMASKML/MPLCMASKML_tutorial.ipynb new file mode 100644 index 00000000..6e0e9573 --- /dev/null +++ b/VAPs/quicklook/MPLCMASKML/MPLCMASKML_tutorial.ipynb @@ -0,0 +1,838 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# MPLCMASKML.C1 Notebook\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/mplcmaskml) for more information about this vap." + ] + }, + { + "cell_type": "markdown", + "id": "97097763", + "metadata": {}, + "source": [ + "In this notebook, we demonstrate the workflow to explore ARM vap data (using mplcmaskml as an example.) Value-added products (VAPs) are higher-order data products that have been analyzed and processed to ease scientists’ use of ARM data in atmospheric research and global climate models. \n", + "Here is the main content we will cover." + ] + }, + { + "cell_type": "markdown", + "id": "eddec40f", + "metadata": {}, + "source": [ + "# Table of Content\n", + "## Access the data\n", + "* How to retrieve the data\n", + "* Data path and file name conventions\n", + "* Load data\n", + "## Explore the data\n", + "* NetCDF Data structure\n", + "* Xarray essentials\n", + "* Xarray Variable\n", + "## Plot the data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "import random\n", + "\n", + "import glob\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "2372d5be", + "metadata": {}, + "source": [ + "## Access the data" + ] + }, + { + "cell_type": "markdown", + "id": "5b0d9684", + "metadata": {}, + "source": [ + "### How to retrieve the data\n", + "We assume the path \"/data/archive\" is available where you are running this notebook. You can use `os.path.exists(\"/data/archive\")` to verify if the path exists at your machine.\n", + "\n", + "\n", + "### Data path and file name conventions\n", + "There several common terminologies regarding ARM data, for example, data-stream-name, data-level, site, facility, instrument, etc. (For more details, please see [ARM Data File Standards Version 1.2](https://www.google.com/search?q=arm+datastream+facility+cite+definition&rlz=1C1GCEJ_enUS1029US1029&ei=hb41ZICrBPukqtsPvMODIA&ved=0ahUKEwjAgeiS0aL-AhV7kmoFHbzhAAQQ4dUDCBA&uact=5&oq=arm+datastream+facility+cite+definition&gs_lcp=Cgxnd3Mtd2l6LXNlcnAQAzIFCAAQogQyBQgAEKIEMgUIABCiBDIFCAAQogQyBQgAEKIEOgoIABBHENYEELADOgoIIRCgARDDBBAKSgQIQRgAUJ4DWIoOYO0PaAFwAXgAgAGMAYgBuQmSAQMyLjmYAQCgAQHIAQjAAQE&sclient=gws-wiz-serp).) \n", + "\n", + "For example, this notebook is called `mplcmaskml.c1`, where `mplcmaskml` is the \"datastream name\", and the `{process.ds_class_level}}` is the \"data level\".\n", + "\n", + "This datastream also contains site `awr` and facility `M1`. (Note: individual datastream might have multiple site-facility pairs.)\n", + "In such a case, the data of this data-stream is stored at `/data/archive/awr/awrmplcmaskmlM1.c1`, which is in the format of `//.`. We can use the following method to assign the data-stream directory `datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )`\n", + "\n", + "The data files under datastream_dir also follows naming conventions. But once reach the datastream_dir level, the most import file naming convention to differentiate the files is \"yyyyMMdd.hhmmss\", which comes handy to filter out files based on datetime. For example, we can use `glob.glob(f'{datastream_dir}/*.200709*.*')` to filter files in 2007 September.\n", + "\n", + "(Note: refer to the https://adc.arm.gov/discovery/#/ and https://adc.arm.gov/solr8/metadata/select API to explore ARM datastream and assoicated available sites and facilities\n", + "\n", + "Please see the following examples in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d7e9eb85", + "metadata": {}, + "outputs": [], + "source": [ + "# Verify if DATA_DIR path exists\n", + "DATA_DIR = \"/data/archive\"\n", + "os.path.exists(DATA_DIR)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "586993fd", + "metadata": {}, + "outputs": [], + "source": [ + "# Speicify datastream_dir following the path conventions and check its existence\n", + "DATASTREAM_NAME = \"mplcmaskml\"\n", + "DATA_LEVEL = \"c1\"\n", + "site = \"awr\"\n", + "facility = \"M1\"\n", + "datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )\n", + "print(datastream_dir)\n", + "print(os.path.exists(datastream_dir))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0742f7c1", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: list 5 (random) files under datastream_dir\n", + "files = os.listdir(datastream_dir)\n", + "files[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39b98a36", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: get most recent file\n", + "list_of_files = glob.glob(f\"{datastream_dir}/*\") # * means all if need specific format then *.csv\n", + "latest_file = max(list_of_files, key=os.path.getctime)\n", + "latest_file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "902d514e", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus sort datastream files based on datetime\n", + "files = os.listdir(datastream_dir)\n", + "file_sorted = files.copy()\n", + "file_sorted.sort() \n", + "print(file_sorted[:5])\n", + "\n", + "# to reverse\n", + "file_sorted_reverse = files.copy() \n", + "file_sorted_reverse.sort(reverse=True)\n", + "print(file_sorted_reverse[:5])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec5923b2", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: pattern matching\n", + "# filter the 200709** files under datastream_dir\n", + "files_filter = glob.glob(f'{datastream_dir}/*.200709*.*')\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c4a4aa18", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: use armnotebook_utils.file_filter (TODO) to filter files based on datastream info \n" + ] + }, + { + "cell_type": "markdown", + "id": "cfeb9efc", + "metadata": {}, + "source": [ + "### Load data\n", + "The arm data is stored in [NetCDF](https://en.wikipedia.org/wiki/NetCDF#:~:text=%22NetCDF%20(network%20Common%20Data%20Form,format%20for%20representing%20scientific%20data.) format. We can use xarray's `open_dataset` method to load single file, or `open_mfdataset` to open multiple files. (Note: the latter will still return a single xarray dataset object by combining multiple files.)\n", + "\n", + "Note: open_dataset keeps the file handle open and lazy loads its contents. All parameters are passed directly to open_dataset. It is a preferable method over load_dataset for memory-effiency. (more details, please see [xarray.open_dataset](https://docs.xarray.dev/en/stable/generated/xarray.open_dataset.html))\n", + "\n", + "See the following example in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a440a329", + "metadata": {}, + "outputs": [], + "source": [ + "# open single file\n", + "full_path = latest_file\n", + "print(full_path)\n", + "ds_single = xr.open_dataset(full_path)\n", + "ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0143a3d", + "metadata": {}, + "outputs": [], + "source": [ + "print(ds_single)\n", + "print(type(ds_single))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c0f8939", + "metadata": {}, + "outputs": [], + "source": [ + "# open multiple files\n", + "n_files = 3\n", + "full_paths = [os.path.join(datastream_dir, f_path) for f_path in file_sorted_reverse[:n_files]]\n", + "print(full_paths)\n", + "try: # Note: sometimes multiple files cannot be merged, so we used try except here.\n", + " ds_mutiple = xr.open_mfdataset(full_paths)\n", + " # ds_mutiple\n", + " print(type(ds_mutiple))\n", + " print(ds_mutiple)\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "3fa59943", + "metadata": {}, + "source": [ + "## Explore the data" + ] + }, + { + "cell_type": "markdown", + "id": "571f69b9", + "metadata": {}, + "source": [ + "### NetCDF Data structure \n", + "\n", + "\n", + "(If you are confident with NetCDF basics and xarray essentials, feel free to skip this session.)\n", + "Before we dive into data exploration, there are some eseential concpets we should be familiar with (shown below): \n", + "* Dataset\n", + "* Data array\n", + "* Variable\n", + "* Dimenssion\n", + "* Coordinate\n", + "* Data Type\n", + "* Meta Data (Attributes)\n", + "\n", + "We will not go into details about NetCDF basics and here are some references you might find helpful\n", + "* [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#)\n", + "* [Network Common Data Form (NetCDF)](https://www.unidata.ucar.edu/software/netcdf/) and [A Brief History of (netCDF) Time](https://www.unidata.ucar.edu/software/netcdf/time/recs.html)\n", + "\n", + "\n", + "\n", + "### Xarray essentials\n", + "Earlier we introduced the xarray pacakge and used `open_dataset` and `open_mfdataset` to retrieve NetCDF data file as an xarray Dataset object. Recall that you can review the data in a notebook by using `print(ds)` or `ds`. \n", + "\n", + "Using xarray to retrieve the aforementioned NetCDF basics is straightforwared. In general,\n", + "* Dataset: `ds`\n", + "* Data array: `ds.variables`\n", + "* Variable: `ds.ds.variables`\n", + "* Dimenssion: `ds.dims`\n", + "* Coordinate: `ds.coords`\n", + "* Data Type: `type`\n", + "* Meta Data (Attributes)\n", + "\n", + "Also, here are some references if you are new to xarray\n", + "* [Xarray in 45 minutes](https://tutorial.xarray.dev/overview/xarray-in-45-min.html)\n", + "* [Handling NetCDF Files using XArray for Absolute Beginners](https://towardsdatascience.com/handling-netcdf-files-using-xarray-for-absolute-beginners-111a8ab4463f)\n", + "\n", + "Try the following commands in action\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b3eac323", + "metadata": {}, + "outputs": [], + "source": [ + "ds = ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77ecf85d", + "metadata": {}, + "outputs": [], + "source": [ + "# Dataset \n", + "ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25e7de09", + "metadata": {}, + "outputs": [], + "source": [ + "# Data array, Variable\n", + "# Note: the info can be overwhelming. Do not attempt to grasp everything at the first glance.\n", + "ds.variables" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c41b67e", + "metadata": {}, + "outputs": [], + "source": [ + "# dimenssions\n", + "ds.dims" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "156f1dfc", + "metadata": {}, + "outputs": [], + "source": [ + "# coordinates\n", + "ds.coords" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "277d6064", + "metadata": {}, + "outputs": [], + "source": [ + "# Meta Data (Attributes)\n", + "ds.attrs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d334681f", + "metadata": {}, + "outputs": [], + "source": [ + "# type\n", + "print(type(ds))\n", + "print(type(ds.variables))\n", + "print(type(ds.dims))\n", + "print(type(ds.coords))\n", + "print(type(ds.attrs))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "643399d6", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: retrieve variable names only\n", + "list(ds.variables)" + ] + }, + { + "cell_type": "markdown", + "id": "1cf36878", + "metadata": {}, + "source": [ + "#### Discussion: variable vs. coordinates vs. dimenssions. \n", + "From [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#):\n", + "\n", + "* Dimession: A dimension may be used to represent a real physical dimension, for example, time, latitude, longitude, or height. A dimension might also be used to index other quantities, for example station or model-run-number.\n", + "A netCDF dimension has both a name and a length. A dimension length is an arbitrary positive integer, except that one dimension in a netCDF dataset can have the length UNLIMITED.\n", + "\n", + "* Variables: Variables are used to store the bulk of the data in a netCDF dataset. A variable represents an array of values of the same type. A scalar value is treated as a 0-dimensional array. A variable has a name, a data type, and a shape described by its list of dimensions specified when the variable is created. A variable may also have associated attributes, which may be added, deleted or changed after the variable is created.\n", + "\n", + "* Coordinate (Variables): It is legal for a variable to have the same name as a dimension. Such variables have no special meaning to the netCDF library. However there is a convention that such variables should be treated in a special way by software using this library.\n", + "A variable with the same name as a dimension is called a coordinate variable. It typically defines a physical coordinate corresponding to that dimension.\n", + "\n", + "\n", + "Tips: By definition, coordinates and dimenssions are also variable. For example ds.time is a coordinate but it is also a special variable. But in practice, when we talk about variable, it implies regular/non-coordinate variable. We will use this convention for the remaining notebook. " + ] + }, + { + "cell_type": "markdown", + "id": "d47120f9", + "metadata": {}, + "source": [ + "### Xarray Variable\n", + "\n", + "\n", + "We can use `ds.variables` to access varialbes of a dataset. But many times viewing print-out the whole datasets can be overwhemling. Instead, we would work on the individual variable (also called Xarray data array.) using the `ds[var_name]` syntax, where `var_name` is the name of the variable. See the following examples in action.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5a57e136", + "metadata": {}, + "outputs": [], + "source": [ + "print(type(ds[\"time\"]))\n", + "ds[\"time\"]" + ] + }, + { + "cell_type": "markdown", + "id": "51ec643e", + "metadata": {}, + "source": [ + "#### Variable properties\n", + "For individual varible, the following properties are mostly used with the assocaited xarray method to retrieve them. (assuming var = ds[var_name])\n", + "* name: `var.name`\n", + "* data content: `var.data`\n", + "* attributes: `var.attrs`\n", + "* dimenstions: `var.dims`\n", + "* data type: `var.data.dtype`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a632a525", + "metadata": {}, + "outputs": [], + "source": [ + "var_name = \"time\"\n", + "var = ds[var_name]\n", + "\n", + "print(\"var.name: \\n\", var.name, \"\\n\")\n", + "print(\"var.data: \\n\", var.data, \"\\n\")\n", + "print(\"var.attrs: \\n\", var.attrs, \"\\n\")\n", + "print(\"var.dims: \\n\", var.dims, \"\\n\")\n", + "print(\"var.data.dtype: \\n\", var.data.dtype, \"\\n\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b256b07f", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: indivdual variable info table (collect and display variable info as a tabular format)\n", + "df_info = pd.DataFrame()\n", + "df_info[\"var_name\"] = list(ds.variables)\n", + "df_info[\"dims\"] = df_info.var_name.apply(lambda x: ds[x].dims)\n", + "df_info[\"is_dim\"] = df_info.var_name.apply(lambda x: x in ds.dims)\n", + "df_info[\"n_dim\"] = df_info.var_name.apply(lambda x: len(ds[x].dims))\n", + "df_info[\"attrs\"] = df_info.var_name.apply(lambda x: ds[x].attrs)\n", + "df_info[\"dtype\"] = df_info.var_name.apply(lambda x: ds[x].data.dtype)\n", + "df_info" + ] + }, + { + "cell_type": "markdown", + "id": "e6a36b96", + "metadata": {}, + "source": [ + "### Data cleaning/Preprocessing (skipped)\n", + "Data cleaning and preprocessing is an important stage in the the data analysis pipeline. However it will be out of the scope of this notebook. For more information about data cleaning and preprocessing basics, here are some references. \n", + "* [Xarray Fundamentals](https://earth-env-data-science.github.io/lectures/xarray/xarray.html)\n", + "* [Xarray Tutorial — Pangeo Gallery documentation](http://gallery.pangeo.io/repos/pangeo-data/pangeo-tutorial-gallery/xarray.html)\n", + "* [Pythonic Data Cleaning With pandas and NumPy](https://realpython.com/python-data-cleaning-numpy-pandas/)\n", + "* [Pandas - Cleaning Data](https://www.w3schools.com/python/pandas/pandas_cleaning.asp)" + ] + }, + { + "cell_type": "markdown", + "id": "ae6e1a69", + "metadata": {}, + "source": [ + "### Plotting\n", + "Data visualization (or plotting) is another important data analysis topic and deserves its own discussion. Here in this notebook we will only demonstrate basic tool for simple data visualization tasks.\n", + "\n", + "Here are some reference you might find useful:\n", + "* xarray plotting: https://docs.xarray.dev/en/stable/user-guide/plotting.html\n", + "* Atmospheric Community Toolkit (ACT): https://arm-doe.github.io/ACT/index.html\n", + "\n", + "Note: this notebook is auto-generated using a template. It uses a general idea to select variable(s) to plot and is not customized to each indivdual notebook. Feel free to change the variables in intrrests, especially certain figure is failed to plot." + ] + }, + { + "cell_type": "markdown", + "id": "7e417fb2", + "metadata": {}, + "source": [ + "#### 1-dimenssional basic time series plot\n", + "\n", + "For the following plot we would like to find variables such that\n", + "* it has one and only one dimession\n", + "* \"time\" is its coordinate variable\n", + "* it is not a dimenssion itself,\n", + "* it is not a special variable with substrings within [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "\n", + "Please see the following example in action. (For more details about pandas filtering, please see the following references.)\n", + "* [pandas: multiple conditions while indexing data frame](https://stackoverflow.com/questions/22591174/pandas-multiple-conditions-while-indexing-data-frame-unexpected-behavior)\n", + "* [pandas.DataFrame.apply](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.apply.html)\n", + "* [How to test if a string contains one of the substrings in a list...](https://stackoverflow.com/questions/26577516/how-to-test-if-a-string-contains-one-of-the-substrings-in-a-list-in-pandas" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30edac2d", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==1, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter = df_info[(df_info.n_dim == 1) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4777a659", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_1d = df_filter.var_name.values[0]\n", + " var_1d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb5ae985", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " ds[var_1d].plot()\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "9e41066e", + "metadata": {}, + "source": [ + "#### 2-dimenssional basic plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09bd4adc", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==2, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_2 = df_info[(df_info.n_dim == 2) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2cdea1a9", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_2d = df_filter_2.var_name.values[0]\n", + " var_2d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5f48879", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " print(ds[var_2d].dims)\n", + " # ds[var_2d].plot()\n", + "\n", + " # conventionally, use \"time\" as x-axis\n", + " ds[var_2d].plot(x=\"time\")\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "7554b2a6", + "metadata": {}, + "source": [ + "#### qc-plotting (optional)\n", + "\n", + "Note: act qc plotting has more strict requirement, one of them is the associated qc variable need to have \"flag_masks\" attributes. Which is added by using ds = act.io.armfiles.read_netcdf(files_list), then ds.clean.cleanup()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71c2096f", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " ds_act = act.io.armfiles.read_netcdf(full_path)\n", + " print(type(ds_act))\n", + " ds_act.clean.cleanup()\n", + "\n", + " # or \n", + " # ds.clean.cleanup()\n", + "except Exception as e:\n", + " print(\"ERROR\", e)\n", + " ds_act = ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "844f8505", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter valid variables for ACT qc plotting\n", + "condition = (\"qc_\" + df_info.var_name).apply(lambda x: ds_act[x].attrs.get(\"flag_masks\") is not None \n", + " if x in list(ds.variables) else False)\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_3 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings))) &\n", + " condition\n", + " ]\n", + "df_filter_3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd38cc6", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "try:\n", + " qc_variable = df_filter_3.var_name.values[0]\n", + " print(qc_variable)\n", + "except Exception as e:\n", + " print(e)\n", + " \n", + "try:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + " qc_ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "d76e4d27", + "metadata": {}, + "source": [ + "#### bonus: choose variables to plot from a dropdown menu " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb733804", + "metadata": {}, + "outputs": [], + "source": [ + "# Valid variables filtering\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_4 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82d5c20d", + "metadata": {}, + "outputs": [], + "source": [ + "# example 1: using xarray plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2fc9aaa4", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# fig, ax = plt.subplots(figsize=(10, 4))\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "# fig.clear() # Remove old lines from plot and plot new one\n", + "# if len(ds[var].dims)==2:\n", + "# ds[var].plot(x=\"time\", add_colorbar=False)\n", + "# else:\n", + "# ds[var].plot()\n", + "# plt.grid()\n", + "# plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fabdd802", + "metadata": {}, + "outputs": [], + "source": [ + "# example 2: using act plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "962b4186", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "\n", + "\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "\n", + "# i_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + "# i_display.add_subplots((1,), figsize=(10, 4))\n", + "# ax = i_display.plot(var, subplot_index=(0,), set_title=f\"{var} ({ds_act[var].attrs['long_name']})\",)\n", + "\n", + "# ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "# ax.grid()\n", + "# plt.show()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/MPLCMASKML/mplcmaskml.c1.ipynb b/VAPs/quicklook/MPLCMASKML/mplcmaskml.c1.ipynb new file mode 100644 index 00000000..795f305a --- /dev/null +++ b/VAPs/quicklook/MPLCMASKML/mplcmaskml.c1.ipynb @@ -0,0 +1,339 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# MPLCMASKML.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/mplcmaskml) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'mplcmaskml'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2017-01-02', 'facility': 'M1', 'site': 'awr', 'start_date': '2015-12-01'}, {'end_date': '2020-06-01', 'facility': 'M1', 'site': 'anx', 'start_date': '2020-02-13'}, {'end_date': '2020-06-01', 'facility': 'S2', 'site': 'anx', 'start_date': '2020-01-03'}, {'end_date': '2021-06-13', 'facility': 'M1', 'site': 'oli', 'start_date': '2013-09-13'}, {'end_date': '2023-12-04', 'facility': 'C1', 'site': 'ena', 'start_date': '2013-10-03'}, {'end_date': '2022-09-29', 'facility': 'M1', 'site': 'hou', 'start_date': '2021-08-13'}, {'end_date': '2019-04-30', 'facility': 'M1', 'site': 'cor', 'start_date': '2018-09-27'}, {'end_date': '2023-12-15', 'facility': 'C1', 'site': 'nsa', 'start_date': '2010-10-09'}, {'end_date': '2023-12-14', 'facility': 'C1', 'site': 'sgp', 'start_date': '2010-07-10'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2023-12-12'\n", + "date_end = '2023-12-14'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['cloud_mask', 'cloud_base', 'cloud_top']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'backscatter'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'cloud_mask'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/MPLNOR/.ipynb_checkpoints/mplnor1camp.c1-checkpoint.ipynb b/VAPs/quicklook/MPLNOR/.ipynb_checkpoints/mplnor1camp.c1-checkpoint.ipynb new file mode 100644 index 00000000..d37d9027 --- /dev/null +++ b/VAPs/quicklook/MPLNOR/.ipynb_checkpoints/mplnor1camp.c1-checkpoint.ipynb @@ -0,0 +1,1732 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# MPLNOR1CAMP.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/mplnor) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'mplnor1camp'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2004-05-11', 'facility': 'C1', 'site': 'sgp', 'start_date': '1996-05-01'}, {'end_date': '1999-11-18', 'facility': 'C2', 'site': 'twp', 'start_date': '1998-11-20'}]" + ] + }, + { + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "ac6764f5", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The following locations and date ranges are available for this VAP:\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
sitefacilitystart_dateend_date
0sgpC11996-05-012004-05-11
1twpC21998-11-201999-11-18
\n", + "
" + ], + "text/plain": [ + " site facility start_date end_date\n", + "0 sgp C1 1996-05-01 2004-05-11\n", + "1 twp C2 1998-11-20 1999-11-18" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2004-05-10'\n", + "date_end = '2004-05-11'" + ] + }, + { + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'/data/archive/sgp/sgpmplnor1campC1.c1'" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['20040510', '20040511']" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "51feea2e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['/data/archive/sgp/sgpmplnor1campC1.c1/sgpmplnor1campC1.c1.20040510.000020.cdf',\n", + " '/data/archive/sgp/sgpmplnor1campC1.c1/sgpmplnor1campC1.c1.20040511.000011.cdf']" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "77 files loaded\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
<xarray.Dataset>\n",
+       "Dimensions:                     (time: 1420, height: 445, nlayers: 5)\n",
+       "Coordinates:\n",
+       "  * height                      (height) float32 0.02998 0.1199 ... 39.88 39.97\n",
+       "  * time                        (time) datetime64[ns] 2004-05-10T00:00:20 ......\n",
+       "Dimensions without coordinates: nlayers\n",
+       "Data variables: (12/20)\n",
+       "    base_time                   datetime64[ns] 2004-05-10T00:00:20\n",
+       "    time_offset                 (time) datetime64[ns] 2004-05-10T00:00:20 ......\n",
+       "    backscatter                 (time, height) float32 dask.array<chunksize=(1420, 445), meta=np.ndarray>\n",
+       "    background_signal           (time) float32 dask.array<chunksize=(1420,), meta=np.ndarray>\n",
+       "    cloud_base_height           (time, nlayers) float32 dask.array<chunksize=(1420, 5), meta=np.ndarray>\n",
+       "    cloud_top_height            (time, nlayers) float32 dask.array<chunksize=(1420, 5), meta=np.ndarray>\n",
+       "    ...                          ...\n",
+       "    detector_temp               (time) float32 dask.array<chunksize=(1420,), meta=np.ndarray>\n",
+       "    instrument_temp             (time) float32 dask.array<chunksize=(1420,), meta=np.ndarray>\n",
+       "    laser_temp                  (time) float32 dask.array<chunksize=(1420,), meta=np.ndarray>\n",
+       "    lat                         float32 ...\n",
+       "    lon                         float32 ...\n",
+       "    alt                         float32 ...\n",
+       "Attributes: (12/24)\n",
+       "    Date:                        Tue May 11 19:11:20 2004\n",
+       "    Version:                     $State: process-vap-mplnor-2.9-0 $\n",
+       "    Command_Line:                mplnor -d 20040510\n",
+       "    Input_Platforms:             sgpmplC1.a1\n",
+       "    BW_Version:                  Working_4_1\n",
+       "    Comment:                     Pass-through VAP to improve the data quality\n",
+       "    ...                          ...\n",
+       "    history:                     created by user dsmgr on machine fore at 11-...\n",
+       "    _file_dates:                 ['20040510']\n",
+       "    _file_times:                 ['000020']\n",
+       "    datastream:                  sgpmplnor1campC1.c1\n",
+       "    _datastream:                 sgpmplnor1campC1.c1\n",
+       "    _arm_standards_flag:         1
" + ], + "text/plain": [ + "\n", + "Dimensions: (time: 1420, height: 445, nlayers: 5)\n", + "Coordinates:\n", + " * height (height) float32 0.02998 0.1199 ... 39.88 39.97\n", + " * time (time) datetime64[ns] 2004-05-10T00:00:20 ......\n", + "Dimensions without coordinates: nlayers\n", + "Data variables: (12/20)\n", + " base_time datetime64[ns] 2004-05-10T00:00:20\n", + " time_offset (time) datetime64[ns] 2004-05-10T00:00:20 ......\n", + " backscatter (time, height) float32 dask.array\n", + " background_signal (time) float32 dask.array\n", + " cloud_base_height (time, nlayers) float32 dask.array\n", + " cloud_top_height (time, nlayers) float32 dask.array\n", + " ... ...\n", + " detector_temp (time) float32 dask.array\n", + " instrument_temp (time) float32 dask.array\n", + " laser_temp (time) float32 dask.array\n", + " lat float32 ...\n", + " lon float32 ...\n", + " alt float32 ...\n", + "Attributes: (12/24)\n", + " Date: Tue May 11 19:11:20 2004\n", + " Version: $State: process-vap-mplnor-2.9-0 $\n", + " Command_Line: mplnor -d 20040510\n", + " Input_Platforms: sgpmplC1.a1\n", + " BW_Version: Working_4_1\n", + " Comment: Pass-through VAP to improve the data quality\n", + " ... ...\n", + " history: created by user dsmgr on machine fore at 11-...\n", + " _file_dates: ['20040510']\n", + " _file_times: ['000020']\n", + " datastream: sgpmplnor1campC1.c1\n", + " _datastream: sgpmplnor1campC1.c1\n", + " _arm_standards_flag: 1" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter[0]\n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['backscatter', 'cloud_base_height', 'cloud_top_height']" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "b62b704e7cb241cea9f9f03bf8d662ea", + "version_major": 2, + "version_minor": 0 + }, + "image/png": "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APzjH//weH7JJZegYcOGeP/9903dxiY7O9sdlHfs2BG7du3SPE6SJLRo0QKvvvoqqlat6n6/y8N9Z86cqVuH2bYrj9F6jRwOh+Z2s/8fEBFFGmZsieiCM23aNFx55ZU4ePCge3gdAHTr1g3XXXcdnnzySRQVFaF9+/buVZFbtmyJO+64I+Rt69atG+x2OwYNGoQxY8aguLgYM2fOxNGjRwMqr2fPnjh27BjefPNN7Nixw2Nfw4YNUatWLTz33HNYsWIF2rVrh4cffhiNGzdGcXExdu/ejSVLluDtt99G3bp1ceedd+LVV1/FnXfeieeffx6NGjXCkiVLsGzZMlNtee655/Dll1/immuuwbhx43D55Zfj2LFjWLp0KUaPHo1LL70Ujz76KObOnYvevXvjueeeQ3Z2NhYvXowZM2bggQce0J3TbEb79u1x3333YdiwYdi0aROuueYaJCUlYf/+/fj2229x+eWX44EHHkCVKlXw8ssv45577kHXrl1x7733Ii0tDb/++iu+//57vPnmmwDgDrKmTZuGnj17wmazoXnz5qbrqVatGh5//HFMnjwZ99xzD26++WYUFBQgNzfXr6HIWl5//XV06NABHTt2xAMPPICLLroIJ06cwK+//oovvvjCa66y0vXXX4/Zs2fj0ksvRfPmzbF582a89NJLXsNxGzZsiISEBHz44Ye47LLLUKVKFWRmZhoGPoMGDcLo0aMxaNAglJSUeM1nN9t3vixYsACxsbHo1q2be1XkFi1a4JZbbgFw9suLatWq4f7778eECRMQFxeHDz/8UPNe12+99Rb69OmDNm3a4NFHH0W9evWwd+9eLFu2zCuABs4OzV2zZg2uu+46XHPNNVixYgWaNWuGRYsWYcaMGbjxxhvRoEEDCCGwYMECHDt2DN26dQMAdOzYEXfccQcmT56MAwcO4Prrr4fD4cDWrVuRmJiIhx56yK+2671H7XY7Lr/8cqxevRpffPEFMjIykJycjMaNG5v+/4CIKOKEZ80qIqLQU66KrDZ48GABwGNVZCGEOHPmjHjyySdFdna2iIuLExkZGeKBBx4QR48e9TguOztb9O7d26tceVXk//u//zPVFuVqqrIvvvhCtGjRQsTHx4s6deqIJ554Qnz55ZcCgFi1apX7ODOrIgPQfShXs/3rr7/Eww8/LOrXry/i4uJE9erVxZVXXimefvppcfLkSfdxf/zxhxgwYICoUqWKSE5OFgMGDBDr1q0ztSqyEGdXt73rrrtEenq6iIuLE5mZmeKWW24RBw4ccB+zZ88eMXjwYFGjRg0RFxcnGjduLF566SWPFWblFVlfeuklrzq0+lTp/fffF61btxZJSUkiISFBNGzYUNx5551i06ZNHsctWbJE5OTkiKSkJJGYmCiaNGkipk2b5t5fUlIi7rnnHlGrVi0hSZIAIPLz8/2qx+VyialTp4qsrCxht9tF8+bNxRdffCFycnJMr4o8cuRIzX35+fnirrvuEnXq1BFxcXGiVq1aol27dmLy5Mle/ah87Y4ePSruvvtuUbt2bZGYmCg6dOgg1q5dq9mmf//73+LSSy8VcXFxAoCYMGGCEMJ7VWQl+Xevffv2utdl9jVSk+vdvHmz6NOnj/t9OmjQII/3mBBCrFu3TrRt21YkJiaKWrVqiXvuuUds2bJF8728fv160bNnT5GamiocDodo2LChx8rLWu+5Y8eOifbt24vq1auLvLw88fPPP4tBgwaJhg0bioSEBJGamiquvvpqMXv2bI+6nE6nePXVV0WzZs2E3W4Xqampom3btuKLL77wu+1G79Ft27aJ9u3bi8TERK9VuM38f2D0O0hEFA6SEAbjrIiIiIiIiIgiHOfYEhERERERUVRjYEtERERERERRjYEtERERERERRTUGtmE2depUSJKEUaNGubcJIZCbm4vMzEwkJCSgU6dOXquZEhERERER0VkMbMMoLy8P77zzDpo3b+6x/cUXX8Qrr7yCN998E3l5eUhPT0e3bt1w4sSJMLWUiIiIiIgocjGwDZOTJ0/itttuw7vvvotq1aq5twsh8Nprr+Hpp59G//790axZM8yZMwenT5/GvHnzwthiIiIiIiKiyBQb7gZcqEaOHInevXuja9eumDx5snt7fn4+CgsL0b17d/c2h8OBnJwcrFu3DsOHD9csr6SkBCUlJe7nLpcLR44cQY0aNSBJUuguhIiIiIiIIITAiRMnkJmZiZgY4/xhcXExSktLDY+x2+2Ij48PZhMrNQa2YTB//nxs2bIFeXl5XvsKCwsBAGlpaR7b09LSsGfPHt0yp06diokTJwa3oURERERE5JeCggLUrVtXd39xcTHqZ1dB4UGnYTnp6enIz89ncGsSA1uLFRQU4JFHHsHy5csN36Tq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kRjYmvTtt9/i22+/RYsWLcLdFCIiIopUDGqJyCQzQ5FTUlJM3ce2WrVqAICqVavi0KFD2LNnD0pLS5GXl4fmzZsHq8kRjYGtSZdeeinOnDkT7mYQEREREVFlEKTFo7p3745//etfyMnJQUlJCV555RXs3bsXOTk5iI+Px9y5c02V8+CDD+LNN980X3GEYWBr0owZM/DUU09h/PjxaNasGeLi4jz2m/kmhYiIiCo5DkUmIovZbDbMmzfPY1u7du0wcOBAv8qJjY3F/fffj7fffjuYzbNMTLgbEC2qVq2K48ePo0uXLqhduzaqVauGatWqoWrVqu7UPxEREV3gGNQSkVni/HBk9cOfjG2wvPbaazh69Cjuuece6ysPAmZsTbrttttgt9sxb948Lh5FREREREQVE6ShyME0ffp0NGrUCP/85z/D04AKYGBr0o8//oitW7eicePG4W4KERERERFFuwgLbE+fPo3+/fvj5Zdftr7yIOBQZJNatWqFgoKCcDeDiIiIiIgqAb1hyEarJYfSLbfcgrvvvhsjR460vvIgYMbWpIceegiPPPIInnjiCVx++eVei0ddKMtoExERERFR5XPjjTdG7fxagIGtabfeeisA4K677nJvkyQJQghIkgSn0xmuphERERERUbSJsKHI0RzUAgxsTcvPzw93E4iIiCjSuQQQwwUmicg3oyHHoR6K/Oeff6JOnTpe20tKSgAADocjtA0IAc6xNSk1NRXZ2dmaj7KysnA3j4iIiCIBg1oi8ofQeYRYVlYWXnnlFa/tq1at8vv+t5GCga1JvXr1QnFxsdf2X375BZ06dbK+QUREREREFL30gloLgtuGDRti3rx5mD59usf2Hj164Keffgpt5SHCwNakatWq4cYbb0R5ebl7208//YROnTphwIABYWwZERERRQze556ITArnqsjJycn4+uuv8fHHH2PKlCke+xITE0NbeYgwsDXpk08+walTpzB48GAIIfDjjz+iU6dOGDRoEF5//fVwN4+IiIgigQjDii9EFJ3CmLEVQiA1NRVff/01Fi9ejCFDhmDz5s146623kJ6eHtrKQ4SBrUnx8fFYtGgRdu3ahZtvvhnXXnst7rzzTs2x6URERHSBcjGwJSJzwpmxjY+PB3A+c1urVi0MGjQIn3/+Od5+++3QVh4iXBXZQFFRkcdzSZLw0UcfoWvX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hcTWZ6tXr9bcuXP117/+VY7j6MILL9Rtt92mfv36pepMmDCh1asbX3nllZo/f74ef/xx9ejRQ5s2bZKU+AWmpKSkw7cBAAAAQAdrx4hte5xyyik65ZRT0somTZqkSZMmZay/bt063XHHHbr77rvbv5JOgMTWZ8ccc4zGjx+vWbNm6Ywzzki7SllS//79s37QJGnWrFmSpFGjRqWVz507V5dccomf4QIAAADIB58S21wdeeSRevnllztuBXlCYuuz999/XwcccECrdbp166a5c+dmnc/1vAAAAICuza9Dkdvy5z//OfXcdV2tWrWqSx4FSmLrs7aSWgAAAACQsRJTtnk+efLJJ1PPQ6GQvvWtb+mxxx7zrf1CQWLrM9d1ddttt+nBBx/Uxo0bFY1G0+Z/+eWXeYoMAAAAQMEI6FDkBx980L/GCpid7wC6mmnTpmnmzJk699xztWXLFl1zzTU666yzZNt2xpspAwAAAIDf3n77bcVird+C6PXXXw8omo5HYuuzv/zlL7rnnnt07bXXKhQK6fzzz9e9996r3/zmN13yJG0AAAAAuUueY5tt2lXvv/++jj32WN1000169dVXtXXrVsViMVVXV2vBggU644wzdN111+36igoEia3PNm3apCOPPFKS1L17d23ZskVS4jLcTY9vBwAAALAbM21Mu+ikk07S4sWLFYlEdNVVV2nvvfdWcXGxjj32WP3973/XNddc06XyE86x9VmfPn30ySefaP/991ffvn317LPPavDgwaqqqlIkEsl3eAAAAAAKQWsjsz6dY7vnnnvqF7/4hX7xi19ISlwPyHEcfxovMIzY+uzMM8/U4sWLJUlXX321fv3rX+uQQw7RRRddpMsuuyzP0QEAAAAoCB08YptJV01qJUZsfXfLLbeknp9zzjnq06ePXnzxRfXt21ennXZaHiMDAAAAUDACuiry7oLEtoMdf/zxOv744/MdBgAAAIAC0tpFovy4eNTuhsTWB0888US76zJqCwAAAAD+IrH1wRlnnNGuepZlyXXdjg0GAAAAQOHjUGRfkdj6wPO8fIcAAAAAoBPhUGR/kdh2gMWLF2vx4sXavHlzWtJrWZbmzJmTx8gAAAAAFAwSWN+Q2Pps2rRpuummmzR06FD17t1blmXlOyQAAAAAhYZDkX1FYuuzu+66S/fff78uvPDCfIcCAAAAoEBx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sQUAAACATuz3v/99vkPIO66KDAAAAADo1EhsAR9EIhHdeOONGc97gP/o72DR38Giv4NFfwePPg8W/Y3dBVdFBgAAAAB0aozYAgAAAAA6NRJbAAAAAECnRmILAAAAAOjUSGwBAAAAAJ0aiS0g6c4779RBBx2k4uJiDRkyRCtWrEjNmzp1qvr166du3bppjz320Lhx4/TKK6+02ea6detUWVmpkpIS7bfffrrpppvU/Fpty5Yt05AhQ1RcXKxvfetbuuuuu3zftkLUWn9L0j//+U+ddtppKi8vV48ePXT88cdr48aNrbZJf2fXWn9/+umnuuSSS7TvvvuqtLRUJ510kt55550226S/M1u+fLlOPfVU7bvvvrIsS4899lhqXiwW0y9+8QsdeeSR6tatm/bdd19ddNFF+vjjj9tsl/7OrLX+lqRLLrlElmWlTccff3yb7dLfmbXV39u2bdNVV12lPn36qKSkRIcffrhmzZrVZrv0d2YzZszQMcccox49euib3/ymzjjjDL311ltpdR555BGdeOKJ2muvvWRZltauXduutulzdEkG2M098MADJhwOm3vuucds2LDBXH311aZbt27mo48+MsYY85e//MUsXLjQvPfee2b9+vVm8uTJpqyszGzevDlrm1u2bDH77LOPmTRpklm3bp15+OGHTY8ePczvf//7VJ3333/flJaWmquvvtps2LDB3HPPPSYcDpuHHnqow7c5n9rq73fffdfsueee5mc/+5lZvXq1ee+998w//vEP8+mnn2Ztk/7OrrX+9jzPHH/88WbEiBHm1VdfNW+++ab5t3/7N7P//vubbdu2ZW2T/s7uqaeeMtdff715+OGHjSTz6KOPpuZ9/fXXZty4cWbBggXmzTffNC+99JI57rjjzJAhQ1ptk/7OrrX+NsaYiy++2Jx00knmk08+SU1ffPFFq23S39m11d+XX365Ofjgg82SJUvMBx98YO6++27jOI557LHHsrZJf2d34oknmrlz55r169ebtWvXmpNPPrnF/nnevHlm2rRp5p577jGSzJo1a9pslz5HV0Vii93esccea/7f//t/aWX9+vUzv/zlLzPW37Jli5FkFi1alLXNO++805SXl5u6urpU2YwZM8y+++5rPM8zxhjz85//3PTr1y9tuR/+8Ifm+OOP39lN6RTa6u/zzjvPfP/738+pTfo7u9b6+6233jKSzPr161Pz4vG42XPPPc0999yTtU36u30y/ePf3KuvvmokpX7YyYT+bp9sie3pp5+eUzv0d/tk6u8BAwaYm266Ka1s8ODB5oYbbsjaDv3dfps3bzaSzLJly1rM++CDD9qd2NLn6Ko4FBm7tWg0qlWrVmnChAlp5RMmTNCLL76Ysf7s2bNVXl6uo446KlV+ySWXaNSoUanXL730kiorK9Nuhn7iiSfq448/1ocffpiq03y9J554olauXKlYLObD1hWetvrb8zw9+eSTOvTQQ3XiiSfqm9/8po477riMhxfS321rq7/r6+slScXFxal5juOoqKhIzz//fKqM/u44W7ZskWVZ+sY3vpEqo7/9tXTpUn3zm9/UoYceqh/84AfavHlz2nz62z8nnHCCnnjiCf3rX/+SMUZLlizR22+/rRNPPDFVh/7eeVu2bJEk7bnnnjktR59jd0Fii93a559/Ltd1tc8++6SV77PPPtq0aVPq9T/+8Q91795dxcXFuu2227Rw4ULttddeqfm9e/fW/vvvn3q9adOmjG0m57VWJx6P6/PPP/dnAwtMW/29efNmbdu2TbfccotOOukkPfvsszrzzDN11llnadmyZan69Hf7tNXf/fr10wEHHKDrrrtOX331laLRqG655RZt2rRJn3zySao+/d0x6urq9Mtf/lIXXHCBysrKUuX0t38mTpyov/zlL3ruuef0hz/8QVVVVRozZkzqRx2J/vbTf//3f6t///7q06ePioqKdNJJJ+nOO+/UCSeckKpDf+8cY4yuueYanXDCCTriiCNyWpY+x+4ilO8AgEJgWVbaa2NMWtno0aO1du1aff7557rnnnt07rnn6pVXXtE3v/lNSYkLPLSnzebl7anTFWXrb8/zJEmnn366fvKTn0iSjj76aL344ou66667VFlZKYn+zlW2/g6Hw3r44Yc1efJk7bnnnnIcR+PGjdPEiRPT6tPf/ovFYpo0aZI8z9Odd96ZNo/+9s95552Xen7EEUdo6NChOuCAA/Tkk0/qrLPOkkR/++m///u/9fLLL+uJJ57QAQccoOXLl+uKK65Q7969NW7cOEn098666qqr9Prrr6cdTdNe9Dl2FyS22K3ttddechwnbXRWkjZv3pz2S2W3bt3Ut29f9e3bV8cff7wOOeQQzZkzR9ddd13Gdnv16pWxTanxV9FsdUKhkHr27LnL21aI2urvvfbaS6FQSP3790+bf/jhh7f6ZU5/Z9aez/eQIUO0du1abdmyRdFoVHvvvbeOO+44DR06NGu79PeuicViOvfcc/XBBx/oueeeSxutzYT+9k/v3r11wAEHtHrlb/p759TW1upXv/qVHn30UZ188smSpIEDB2rt2rX6/e9/n0psm6O/2/bjH/9YTzzxhJYvX64+ffrscnv0OboqDkXGbq2oqEhDhgzRwoUL08oXLlyob3/721mXM8akHcrW3LBhw7R8+XJFo9FU2bPPPqt9991XBx54YKpO8/U+++yzGjp0qMLh8E5sTeFrq7+Liop0zDHHtLidwdtvv60DDjgga7v0d2a5fL7Ly8u1995765133tHKlSt1+umnZ22X/t55yaT2nXfe0aJFi9r1DyL97Z8vvvhC1dXV6t27d9Y69PfOicViisVisu30fy0dx0kdjZMJ/Z2dMUZXXXWVHnnkET333HM66KCDfGmXPkeXFeilqoAClLwdypw5c8yGDRvMlClTTLdu3cyHH35otm3bZq677jrz0ksvmQ8//NCsWrXKTJ482UQikbQryf7yl780F154Yer1119/bfbZZx9z/vnnm3Xr1plHHnnElJWVZbyU/k9+8hOzYcMGM2fOnN3iUvqt9bcxxjzyyCMmHA6b2bNnm3feecf88Y9/NI7jmBUrVqTaoL/br63+fvDBB82SJUvMe++9Zx577DFzwAEHmLPOOiutDfq7/bZu3WrWrFlj1qxZYySZmTNnmjVr1piPPvrIxGIxc9ppp5k+ffqYtWvXpt2Cpr6+PtUG/d1+rfX31q1bzU9/+lPz4osvmg8++MAsWbLEDBs2zOy3336mpqYm1Qb93X6t9bcxxlRWVpoBAwaYJUuWmPfff9/MnTvXFBcXmzvvvDPVBv3dfj/60Y9MeXm5Wbp0adr+YseOHak6X3zxhVmzZo158sknjSTzwAMPmDVr1phPPvkkVYc+x+6CxBYwxvzpT38yBxxwgCkqKjKDBw9OXUq/trbWnHnmmWbfffc1RUVFpnfv3ua0004zr776atryF198samsrEwre/31182IESNMJBIxvXr1MlOnTk1dRj9p6dKlZtCgQaaoqMgceOCBZtasWR26nYUiW38nzZkzx/Tt29cUFxebo446qsU9EOnv3LTW3//1X/9l+vTpY8LhsNl///3NDTfckJZkGUN/52LJkiVGUovp4osvTt2OI9O0ZMmSVBv0d/u11t87duwwEyZMMHvvvXfq833xxRebjRs3prVBf7dfa/1tjDGffPKJueSSS8y+++5riouLzWGHHWb+8Ic/pPUd/d1+2fYXc+fOTdWZO3duxjo33nhjqg59jt2FZUzDmeAAAAAAAHRCnGMLAAAAAOjUSGwBAAAAAJ0aiS0AAAAAoFMjsQUAAAAAdGoktgAAAACATo3EFgAAAADQqZHYAgAAAAA6NRJbAAAAAECnRmILAAAAAOjUSGwBAAAAAJ0aiS0AAAAAoFMjsQUAAAAAdGoktgCAgvH666/r0ksv1UEHHaTi4mJ1795dgwcP1m9/+1t9+eWXqXqjRo3SqFGj8hLj1KlTZVlWm/Xmz5+v22+/veMDyuLDDz+UZVn6/e9/71ubS5culWVZWrp0aYfG89RTT2nq1Km5BwgA2G2R2AIACsI999yjIUOGqKqqSj/72c/09NNP69FHH9V3v/td3XXXXZo8eXK+Q8xJvhPbjjB48GC99NJLGjx4cIeu56mnntK0adM6dB0AgK4llO8AAAB46aWX9KMf/Ujjx4/XY489pkgkkpo3fvx4/fSnP9XTTz+dxwghSWVlZTr++OPzHQYAAC0wYgsAyLv//M//lGVZmj17dlpSm1RUVKTTTjut1Ta+/PJLXXHFFdpvv/1UVFSkb33rW7r++utVX1+fqpM8HPb+++9vsbxlWS0Of33yySd19NFHKxKJ6KCDDmr3Yb2jRo3Sk08+qY8++kiWZaWmXGJNxnTVVVfp7rvv1qGHHqpIJKL+/fvrgQceaFccSTNnztRBBx2k7t27a9iwYXr55Zdb1Fm5cqVOO+007bnnniouLtagQYP04IMPptXJdijyPffckxbf/Pnzdckll+jAAw/MOZ5LLrlEf/rTn1Lbn5w+/PDDnLYZALB7YcQWAJBXruvqueee05AhQ1RRUbFTbdTV1Wn06NF67733NG3aNA0cOFArVqzQjBkztHbtWj355JM5t7l48WKdfvrpGjZsmB544AG5rqvf/va3+vTTT9tc9s4779S//du/6b333tOjjz66S7E+8cQTWrJkiW666SZ169ZNd955p84//3yFQiGdc845bcbypz/9Sf369UsdFv3rX/9a3/nOd/TBBx+ovLxckrRkyRKddNJJOu6443TXXXepvLxcDzzwgM477zzt2LFDl1xySdb2Z8+erR/+8Ic6++yzddttt2nLli2aNm1aiyS9vfH8+te/1vbt2/XQQw/ppZdeSi3Xu3fvNrcVALAbMwAA5NGmTZuMJDNp0qR2L1NZWWkqKytTr++66y4jyTz44INp9W699VYjyTz77LPGGGM++OADI8nMnTu3RZuSzI033ph6fdxxx5l9993X1NbWpspqamrMnnvuadrz9XnyySebAw44oEV5e2NNxlRSUmI2bdqUKovH46Zfv36mb9++ra4/ua1HHnmkicfjqfJXX33VSDJ//etfU2X9+vUzgwYNMrFYLK2NU045xfTu3du4rmuMMWbJkiVGklmyZIkxxhjXdU2vXr3Mcccdl7bcRx99ZMLhcNr25xLPlVde2a4+BgAgiUORAQCd3nPPPadu3bq1GMFMjjQuXrw4p/a2b9+uqqoqnXXWWSouLk6V9+jRQ6eeemqgsY4dO1b77LNP6rXjODrvvPP07rvv6v/+7//aXN/JJ58sx3FSrwcOHChJ+uijjyRJ7777rt58801973vfkyTF4/HU9J3vfEeffPKJ3nrrrYxtv/XWW9q0aZPOPffctPL9999fw4cP36l4AADYGSS2AIC82muvvVRaWqoPPvhgp9v44osv1KtXrxa34fnmN7+pUCikL774Iqf2vvrqK3mep169erWYl6msI2NtLYb2bFfPnj3TXifPYa6trZWk1KHV1157rcLhcNp0xRVXSJI+//zzrNsiKS3xTspU1p54AADYGZxjCwDIK8dxNHbsWP3v//6v/u///k99+vTJuY2ePXvqlVdekTEmLWHcvHmz4vG49tprL0lKjb42P/+zeYK4xx57yLIsbdq0qcW6MpV1RKytrS9Z1jxJ3BnJ9V133XU666yzMtY57LDDMpYn15/pvONd7ScAAHLBiC0AIO+uu+46GWP0gx/8QNFotMX8WCymv//971mXHzt2rLZt26bHHnssrXzevHmp+VJiFLG4uFivv/56Wr3HH3887XW3bt107LHH6pFHHlFdXV2qfOvWra3G0VQkEsk4CtneWJMWL16clji6rqsFCxbo4IMP3qkfAZo77LDDdMghh+i1117T0KFDM049evTIumyvXr1aXD1548aNevHFF3c6JkZxAQC5YsQWAJB3w4YN06xZs3TFFVdoyJAh+tGPfqQBAwYoFotpzZo1mj17to444ois57dedNFF+tOf/qSLL75YH374oY488kg9//zz+s///E995zvf0bhx4yQlbh/z/e9/X/fdd58OPvhgHXXUUXr11Vc1f/78Fm1Onz5dJ510Uuo+uq7r6tZbb1W3bt305ZdftrlNRx55pB555BHNmjVLQ4YMkW3bGjp0aLtjTdprr700ZswY/frXv05dFfnNN9/M+ZY/rbn77rs1ceJEnXjiibrkkku033776csvv9Q///lPrV69Wn/7298yLmfbtqZNm6Yf/vCHOuecc3TZZZfp66+/1rRp09S7d2/Z9s79fn7kkUdKkm699VZNnDhRjuNo4MCBKioq2ultBAB0bSS2AICC8IMf/EDHHnusbrvtNt16663atGmTwuGwDj30UF1wwQW66qqrsi5bXFysJUuW6Prrr9fvfvc7ffbZZ9pvv/107bXX6sYbb0yr+4c//EGS9Nvf/lbbtm3TmDFj9I9//KPFPVfHjx+vxx57TDfccIPOO+889erVS1dccYVqa2s1bdq0Nrfn6quv1htvvKFf/epX2rJli4wxMsbkFKsknXbaaRowYIBuuOEGbdy4UQcffLD+8pe/6LzzzmtHr7bP6NGj9eqrr+rmm2/WlClT9NVXX6lnz57q379/iwtDNfdv//ZvsixLv/3tb3XmmWfqwAMP1C9/+Us9/vjj2rhx407Fc8EFF+iFF17QnXfeqZtuuknGGH3wwQdZ74sLAIBljDH5DgIAALRkWZauvPJK3XHHHfkOJSdff/21Dj30UJ1xxhmaPXt2vsMBAOwGGLEFAAA7bdOmTbr55ps1evRo9ezZUx999JFuu+02bd26VVdffXW+wwMA7CZIbAEAwE6LRCL68MMPdcUVV+jLL79UaWmpjj/+eN11110aMGBAvsMDAOwmOBQZAAAAANCpcbsfAAAAAECnRmILAAAAAJ3Y0qVLNXbsWFVWVurxxx/XggULNGzYMI0ZM0bV1dX5Di8QHIoMAAAAAJ1UXV2dvvvd7+rhhx9WUVGRYrGYTjjhBK1YsUJVVVX685//vFtcoZ6LR3VRnufp448/Vo8ePWRZVr7DAQAAALo0Y4y2bt2qfffdV7bd+oGxdXV1ikajbbbX/P/4SCSiSCSSVvbiiy+qpKREp556qkpLS/Wzn/1MAwYMUFFRkYYPH65rr7125zaokyGx7aI+/vhjVVRU5DsMAAAAYLdSXV2tPn36ZJ1fV1engw7ork2b3Vbb6d69u7Zt25ZWduONN2rq1KlpZZ9++qk++OADvfDCC1q8eLGmTp2q/v37p+a7buvr6SpIbLuoHj16SJJO0HcUUjjP0XR9oZKwLptzlu6b/IjitbF8h9Pl0d/Bor+DRX8Hy8/+Du3bS/GPN/kUWdfFZzxY9Hdw4orpeT2V+j88m2g0qk2bXX2w6gCV9cg8sluz1dNBQz5SdXW1ysrKUuXNR2sl6Rvf+IZOOOEEFRUVacyYMbrooovSEmvHcXZyizoXEtsuKnnYQkhhhSwS244WtsIqLS1V2ApLHPnd4ejvYNHfwaK/g+Vnf4fsiMR3bpv4jAeL/g5Qw5WL2nsaYLfuiSkTt6GtsrKytMQ2k2OPPVa33367JGnNmjWaMGGCNmzYoGg0qqqqKg0cOLBd8XR2JLYAAAAAEDBPRp4yX8c3W3kmPXv21GmnnaaRI0fKtm3dd999evXVV1VZWani4mLNmzfPr5ALGoktAAAAAATMkyevlXm5uPLKK3XllVemXn/rW9/SpEmTdiG6zof72AIAAAAAOjVGbAEAAAAgYK4xck3mQ46zlSM7ElsAAAAACJhf59gigcQWAAAAAALmycglsfUNiS0AAAAABIwRW3+R2AIAAABAwDjH1l8ktgAAAAAQMK9hyjYPuSGxBQAAAICAua2cY5utHNmR2AIAAABAwFyTmLLNQ25IbAEAAAAgYByK7C8SWwAAAAAImCdLrqys85AbElsAAAAACJhnElO2ecgNiS0AAAAABMxtZcQ2WzmyI7EFAAAAgICR2PqLxBYAAAAAAuYZS57Jco5tlnJkR2ILAAAAAAFjxNZfJLYAAAAAEDBXtlzZWeYhV5l7EgAAAACAToLEthOYMWOGLMvSlClT8h0KAAAAAB+YhnNsM02Gc2xzxqHIBa6qqkqzZ8/WwIED8x0KAAAAAJ9wjq2/GLEtYNu2bdP3vvc93XPPPdpjjz3yHQ4AAAAAn7jGbnVCbhixLWBXXnmlTj75ZI0bN07/8R//0Wrd+vp61dfXp17X1NRIkkIlYYWtcIfGCSlcEkp7RMeiv4NFfweL/g6Wn/0dKnZklfCd2xY+48GivwNkJNW2v7onS16WcUZPxp+YdiOWMYZeK0APPPCAbr75ZlVVVam4uFijRo3S0Ucfrdtvvz1j/alTp2ratGktyufPn6/S0tIOjhYAAADYve3YsUMXXHCBtmzZorKysqz1ampqVF5erideP1jdejgZ62zf6uq0ge+12RYa8dNNAaqurtbVV1+tZ599VsXFxe1a5rrrrtM111yTel1TU6OKigrdN/kRRmwDEC4J6bI5Z+u+yQ8rVhvPdzhdHv0dLPo7WPR3sPzs79C+vRT/eJNPkXVdfMaDRX8HJ2ZiOdVv7ZBjl7HHnJHYFqBVq1Zp8+bNGjJkSKrMdV0tX75cd9xxh+rr6+U46b/uRCIRRSKRFm3Fa2Pi3PPgxGrjitXmtlPDzqO/g0V/B4v+DpYf/W3q3MT3LtqFz3iw6O+OF88xsU0cipz5H/Vs5ciOxLYAjR07VuvWrUsru/TSS9WvXz/94he/aJHUAgAAAOhcPNlyOcfWNyS2BahHjx464ogj0sq6deumnj17tigHAAAA0PlwKLK/SGwBAAAAIGCebK6K7CMS205i6dKl+Q4BAAAAgE9cY8k1mc+lzVaO7EhsAQAAACBgbivn2LqM2OaMxBYAAAAAAuYZW16Wc2w9zrHNGYktAAAAAASMEVt/kdgCAAAAQMA8ZT+X1gs2lC6BxBYAAAAAAtb6VZEzlyM7egwAAAAA0KkxYgsAAAAAAXONLTfLxaOylSM7ElsAAAAACJgnS56ynWPLfWxzxU8BAAAAABCw5Ihttqm9PvzwQ+29994aNWqURo0apc8++0wLFizQsGHDNGbMGFVXV3fgVhQORmwBAAAAIGCt3+4nt/HHyspKPfTQQ5KkWCymmTNnasWKFaqqqtL06dM1e/bsXY630DFiCwAAAAAB84zV6pSLF154QSNGjNCvfvUrvf322xowYICKioo0fPhwrVu3roO2oLAwYgsAAAAAAfNaGbFN3u6npqYmrTwSiSgSiaSV9e7dW++++65KS0v1gx/8QI8//rjKyspS813X9TnywsSILQAAAAAEzDN2q5MkVVRUqLy8PDXNmDGjRTuRSETdunWTZVk6++yztWbNmrSE2HGcwLYpnxixBQAAAICAubLkZrn6cbK8uro6bfS1+WitJG3dulU9evSQJC1fvlynnHKKZs2apWg0qqqqKg0cOLADoi88JLYAAAAAELCmI7OZ5klSWVlZWmKbyfPPP68bbrhBpaWlOuiggzR9+nRFIhFVVlaquLhY8+bN8z32QkRiCwAAAAABc6VWRmzbb+LEiZo4cWJa2aRJkzRp0qSdD64TIrEFAAAAgIC1Z8QW7UdiCwAAAAABc40tN0sCm60c2ZHYAgAAAEDAjCx5WQ5FNlnKkR2JLQAAAAAEjBFbf5HYAgAAAEDAPGPJM5lHZrOVIzsSWwAAAAAImCtbrrKM2GYpR3b0GAAAgA/i//pYof32zXcYADqJ5Ihttgm5IbEFAHR50ZOOCWw97tgh7Vpv7MRjMr6Ojx2c9ti8XrYy5F+ody/F//VxvsMAgN0ShyIDAAAAQMA82fKyjDNmK0d2JLYAAAAAEDDXWHKzHHKcrRzZkdgCAAAAQMC4KrK/SGwBAAAAIGDG2PKy3K/WcB/bnJHYAgAAAEDAXFlyleVQ5CzlyI7EFgAAAAAC5pnshxx7JuBgugASWwAAAAAImNfKocjZypEdiS0AAAAABMyTJS/LIcfZypEdiS0AAAAABIzb/fiLxBYAAAAAAsahyP4isQUAAACAgHlq5T62HIqcMxJbAAAAAAiYaeUcW0NimzMSWwAAAAAImGdaGbHlHNuckdgCAAAAQMA4x9ZfJLYAAAAAEDBGbP1FYgsAAAAAAeM+tv5ijBsAAAAA0KkxYgsAAAAAAeNQZH+R2AIAAABAwEhs/cWhyAVo1qxZGjhwoMrKylRWVqZhw4bpf//3f/MdFgAAAACfJBPbbBNyQ2JbgPr06aNbbrlFK1eu1MqVKzVmzBidfvrpeuONN/IdGgAAAAAfkNj6i0ORC9Cpp56a9vrmm2/WrFmz9PLLL2vAgAF5igoAAACAX4yyX/3YBBtKl0BiW+Bc19Xf/vY3bd++XcOGDct3OAAAAAB8wDm2/iKxLVDr1q3TsGHDVFdXp+7du+vRRx9V//79s9avr69XfX196nVNTY0kKVQSVtgKd3i8u7twSSjtER2L/g5Wl+jvsK1wSQD7wrAtR5KdXFcr67XCtkJN5iVfW+HEWULhsC3T8DrUrI1MZdg5fn6+Q8WOLN6XNnWJfUonQn8HyEiqbX91Elt/WcYYRroLUDQa1caNG/X111/r4Ycf1r333qtly5ZlTW6nTp2qadOmtSifP3++SktLOzpcAAAAYLe2Y8cOXXDBBdqyZYvKysqy1qupqVF5eblG/v0KhbpFMtaJb6/X8lPvbLMtNCKx7STGjRungw8+WHfffXfG+ZlGbCsqKjSu5BxGbAMQLgnpsjln677JDytWG893OF0e/R2srtDfsXGDFV60OpD1ODFP9rK1ba43PnawQotXt3htnTRU/3bRwZo97z2Zp1e2qJdpWew8Pz/foV7fVHzTZp8i67q6wj6lM6G/gxMzMS2qfajdie0JT1zZamL7/Gl/IrHNAcckdBLGmLTEtblIJKJIpOUfRrw2piznpKMDxGrjitXG8h3GboP+DlZn7u9ozJMCiD0a8+TEPDkN62ptvbGYJ9NkXvK1FfPSXjevl2lZ7Do/Pt+mzk1876JdOvM+pTOivzte3OTWv8ZYMlkOOc5WjuxIbAvQr371K02cOFEVFRXaunWrHnjgAS1dulRPP/10vkMDAAAA4ANPVtarImcrR3YktgXo008/1YUXXqhPPvlE5eXlGjhwoJ5++mmNHz8+36EBAAAA8AEXj/KXne8A0NKcOXP04Ycfqr6+Xps3b9aiRYtIagEAAIAuJHkocrYpV3/961+19957S5IWLFigYcOGacyYMaqurvY79IJEYgsAAAAAAUuO2GabcmrL8/TQQw+poqJCsVhMM2fO1LJlyzR9+nRNnz69g7agsJDYAgAAAEDA/ByxnT9/vs455xzZtq133nlHAwYMUFFRkYYPH65169Z10BYUFhJbAAAAAAiYaWW0NpnY1tTUpE2Z7pLiuq4efPBBnXfeeZKkr7/+Ou0WQa7rBrNBeUZiCwAAAAAFqKKiQuXl5alpxowZLer8z//8j84991zZdiK122OPPVRTU5Oa7zhOYPHmE1dFBgAAAICAGUnGZJ8nSdXV1Wmjr5FIpEXdDRs2aM2aNfqf//kfvfPOO5o9e7Y2bNigaDSqqqoqDRw40P/gCxCJLQAAAAAEzJMlq4372JaVlaUltpnceuutqedDhw7VbbfdpgceeECVlZUqLi7WvHnz/Au6gJHYAgAAAEDAWrtI1M7c7keSVq5cKUmaNGmSJk2atNOxdUYktgAAAAAQMM9YsrIksLne7gcktgAAAAAQOGNaOcc2SzmyI7EFAAAAgIB1xKHIuzMSWwAAAAAIGImtv0hsAQAAACBgnGPrLxJbAAAAAAgY59j6i8QWAAAAAAKWSGyzHYoccDBdAIktAAAAAASMc2z9RWILAAAAAAEzDVO2ecgNiS0AAAAABIwRW3+R2AIAAABA0Biy9RWJLQAAAAAErZURWzFimzMSWwAAAAAIGLf78Zed7wAAAAAAANgVjNgCAAAAQMC4eJS/GLEFAHR5RU9XKXrSMYGsxw3bcscOaXO94WeqFDvxmBavQ4tXp8piJx7Tol6mZVEY4p9sUqh3r3yHAaCzMFbrE3LCiC0AoKDFxw9VaOHKnV4uetIxst3cT1ZKJqfO4lWp18nnmeolhepc2UtXpyW32dpvmqBaDTG6owalv25WT5LsuCepMfFFYXB69FD8k035DgPtEJswVOFnc9+vdEZOt245LxMfN1ShRY39444eImdJy/0fdg3n2PqLEVsAAAAACJppY9pF27Zt0x133KHRo0erZ8+eKi4uVt++fXX55Zerqqrr/ShKYgsAAAAAAUueY5tt2hXPPfecvv3tb2vjxo268cYb9cYbb+jTTz/Vk08+qeHDh+u6667T2Wef7dOWFAYORe5grutq3bp1OuCAA7THHnvkOxwAAAAAhaKDDjnu1auXXnnlFZWUlKSVl5eX67DDDtOll16qV155pWNWnieM2PpsypQpmjNnjqREUltZWanBgweroqJCS5cuzW9wAAAAAApCR47Y9u/fv0VS29xxxx23S+soNIzY+uyhhx7S97//fUnS3//+d33wwQd68803NW/ePF1//fV64YUX8hwhAAAAgLxr7Vxan0ZyL7vsMplWrkQ1d+5cf1ZUAEhsffb555+rV6/Epf6feuopffe739Whhx6qyZMn67//+7/zHB0AAACAwmA1TNnm7bqhQ4dKklavXq21a9fqsssu86XdQkRi67N99tlHGzZsUO/evfX000/rzjvvlCTt2LFDjuPkOToAAAAABSGAEdsrrrhCb775pm6//Xbttdde2nPPPTVp0iR/Gi8wJLY+u/TSS3Xuueeqd+/esixL48ePlyS98sor6tevX56jAwAAAFAQAkhs33//fZ122mmaO3euBg4cqFGjRmm//fbTiBEj/FlBASGx9dnUqVN15JFHauPGjfrud7+rSCQiSXIcR7/85S/zHB0AAACAgmCsxJRtng9OOeUU3XnnnRo+fLgk6bHHHtPEiRO1fv16X9ovJCS2PorFYpowYYLuvvvuFveFuvjii/MUFQAAAIBCY0xiyjbPD7fccovGjRuXel1RUaF58+b503iBqi+JCQAAQTtJREFUIbH1UTgc1vr162VZ/vzCAgAAAKCLCuBQ5KOOOkofffRRWlnPnj39abzAkNj67KKLLtKcOXN0yy235DsUAAAAAIUqgEORjznmGBljZFmW6uvrtXXrVvXs2VOfffaZL+0XEhJbn0WjUd17771auHChhg4dqm7duqXNnzlzZp4iAwAAAFAoLJOYss3zw+bNm9NeP/3003rhhRf8abzAkNj6bP369Ro8eLAk6e23306bxyHKAAAAAPLlpJNO0q9+9StNnz4936H4jsTWZ0uWLMl3CAAAAAAKXQDn2C5btiz13HVdrV69WnV1df40XmBIbDvIu+++q/fee08jR45USUlJ6th2AAAAAAjiHNuf/exnqed1dXWqrq7WokWLfGm70JDY+uyLL77QueeeqyVLlsiyLL3zzjv61re+pcsvv1zf+MY39Ic//CHfIQIAAADItwBGbF999dW01+vWrdMdd9yhu+++258VFBA73wF0NT/5yU8UDoe1ceNGlZaWpsrPO+88Pf3003mMDAAAAEDBMG1MHeDII4/Uyy+/3DGN5xkjtj579tln9cwzz6hPnz5p5YccckiLe0gBAAAA2E0FMGL75z//OfXcdV2tWrVKJSUl/jReYEhsfbZ9+/a0kdqkzz//XJFIJA8RAQAAACg4AZxj++STT6aeh0Ihfetb39Jjjz3mS9uFhsTWZyNHjtS8efNSl9C2LEue5+l3v/udRo8e3a42ZsyYoUceeURvvvmmSkpK9O1vf1u33nqrDjvssI4MHQAAAEBAgriP7YMPPuhPQ50A59j67He/+53uvvtuTZw4UdFoVD//+c91xBFHaPny5br11lvb1cayZct05ZVX6uWXX9bChQsVj8c1YcIEbd++vYOjBwAAABAIn86xXb9+vYYPH67KykqdfPLJ2rZtmxYsWKBhw4ZpzJgxqq6u7oDgCw8jtj7r37+/Xn/9dc2aNUuO42j79u0666yzdOWVV6p3797taqP5Rabmzp2rb37zm1q1apVGjhzZEWEDAAAA6IQOO+wwvfDCC5KkadOm6dFHH9Udd9yhFStWqKqqStOnT9fs2bPzHGXHI7HtAL169dK0adN8a2/Lli2SpD333DNrnfr6etXX16de19TUSJJCJWGFrbBvsSCzcEko7REdi/4OVr772w7bckpy34+llgvbsm0jz7EUzqEdJ5w4qMluWMYJ26nnmeolWY6RVRJusXym5eySsKyGepZtZBxLYTtxXlU4bMt4RraltHqSZFuSF0q8Du1E36CRn59vpzgkO8770ZZ871MkyQrbu83fTrg49/5uvt91ijLv/9CMkVTb/uqWWjkUueEx+T99UiQSaXHdnnC48b3ZsWOH9t9/fw0YMEBFRUUaPny4rr322vYH1YlZxpgOupj07unAAw/UZZddpksvvVQVFRW73J4xRqeffrq++uorrVixImu9qVOnZkym58+fn/FiVgAAAAD8s2PHDl1wwQXasmWLysrKstarqalReXm5DrjlZtnFxRnreHV1+uiX17cov/HGGzV16tQW5QsXLtTPf/5zhcNh3X777XrwwQd1++2364orrtCyZcv0xhtv7PR2dRYMd/jspz/9qe6//37ddNNNGj16tCZPnqwzzzxzp6+IfNVVV+n111/X888/32q96667Ttdcc03qdU1NjSoqKnTf5EcYsQ1AuCSky+acrfsmP6xYbTzf4XR59Hew8t3f7uhBcpas2enlYuMGy3YbRmwXrW738l7l0ZIke9na1Ovk80z1kizPyFrxWovlMy1nL1ur+NjBieXcxIhtkW3pB5f21T1z31XUM7LjXlo9SbLjXuOI7eL2bxNa8vPz7XTvLnfbNp8i67ryvU+RpPiYQQo9l/t+pTMq7lmui/94Uk797Y4aJGdpY/94I4+WvXxtxwTYhcRMLLcF2nG7n+rq6rQkOVtOMX78eK1Zs0a//e1vtWzZstRIb/fu3fXuu+9q0KBBuuyyy/T9739fe+yxR25xdhIktj778Y9/rB//+Md67bXXdN999+nf//3fdcUVV+iCCy7QZZddpsGDB7fdSJO2nnjiCS1fvrzFfXGby3RYgiTFa2ONxzKgw8Vq44rV5rhTw06jv4OVr/6OxzyFdmK9yeWiMS+R2HqWlEM7bsyTJDkNy7gxL/U8U70kyzWya2Mtls+0nFMbU6yhnuUaGc+S1XAocizmpRLbpvWkhsS24Z8ew9+AL/z4fHuhuFzej3bL5z48FvN2m78dpy6RzObS3833u2408/4P6eIdkNiWlZW1OvorJU5JTOYB5eXlikaj2rBhg6LRqE4//XRt2bJFf//73/Xyyy/rxhtv1IknnqjJkydr3LhxucVb4Lgqcgc56qij9F//9V/617/+pRtvvFH33nuvjjnmGB111FG677771NoR4MYYXXXVVXrkkUf0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bLGVK1fakCFDrKysrN4+qXd69dXbzGzSpEk2duxY27dvX+Ln4MGD9fZJvdNrqN4///nP7fzzz7fy8nLbuXOn/fWvf7VwOGwLFy5M2yf1Tm/MmDE2b94827hxo61fv96uueaaOufnv//97/boo4/anDlzTJJVVFQ02C81x5mKYIuz3uWXX25Tp05NmlZaWmoPPvhgyvaHDx82Sfb++++n7XP27NlWUFBgVVVViWlPPPGEFRUVme/7Zmb2m9/8xkpLS5OW+9WvfmVDhw492V0JhIbqPXHiRPvpT3/apD6pd3r11Xvr1q0myTZu3JiY53meFRYW2pw5c9L2Sb0bJ9Ub/9pWr15tkhIf7KRCvRsnXbAdN25ck/qh3o2Tqt79+/e3xx57LGnapZdeag8//HDafqh34x04cMAk2dKlS+vM27lzZ6ODLTXHmYpLkXFWi0QiWrdunUaPHp00ffTo0VqxYkXK9i+88IIKCgp0ySWXJKZPnjxZV111VeLxypUrNWLEiKQvQx8zZoz27t2rXbt2JdrUXu+YMWO0du1aua7bDHt3+mmo3r7v6+2339aFF16oMWPGqEuXLhoyZEjKywupd8Maqvfx48clSXl5eYl54XBYOTk5+vDDDxPTqPepc/jwYTmOo/bt2yemUe/mtWTJEnXp0kUXXnihfvGLX+jAgQNJ86l387nyyiv15ptv6ssvv5SZqby8XJ9++qnGjBmTaEO9T97hw4clSYWFhU1ajprjbEGwxVnt66+/VjQaVdeuXZOmd+3aVfv37088fuutt9S2bVvl5eXpT3/6kxYtWqROnTol5nfv3l3nnntu4vH+/ftT9lk9r742nufp66+/bp4dPM00VO8DBw7o6NGjevLJJzV27Fi99957uv766zVhwgQtXbo00Z56N05D9S4tLVVJSYlmzJihb775RpFIRE8++aT279+vffv2JdpT71OjqqpKDz74oG6++Wbl5+cnplPv5nP11Vfr5Zdf1uLFi/X0009rzZo1+v73v5/4UEei3s1p1qxZ6tevn4qLi5WTk6OxY8dq9uzZuvLKKxNtqPfJMTNNnz5dV155pQYMGNCkZak5zhZZLb0BwOnAcZykx2aWNG3kyJFav369vv76a82ZM0c33HCDVq1apS5dukiK3eChMX3Wnt6YNmeidPX2fV+SNG7cON17772SpIEDB2rFihV6/vnnNWLECEnUu6nS1Ts7O1vz58/XlClTVFhYqHA4rFGjRunqq69Oak+9m5/rurrxxhvl+75mz56dNI96N5+JEycm/j9gwAANHjxYJSUlevvttzVhwgRJ1Ls5zZo1Sx999JHefPNNlZSUaNmyZZo2bZq6d++uUaNGSaLeJ+vOO+/UJ598knQ1TWNRc5wtCLY4q3Xq1EnhcDhpdFaSDhw4kPRJZZs2bdS7d2/17t1bQ4cO1QUXXKC5c+dqxowZKfvt1q1byj6lE5+KpmuTlZWljh07fud9Ox01VO9OnTopKytL/fr1S5rft2/fel/MqXdqjTm+y8rKtH79eh0+fFiRSESdO3fWkCFDNHjw4LT9Uu/vxnVd3XDDDdq5c6cWL16cNFqbCvVuPt27d1dJSUm9d/6m3iensrJSDz30kN544w1dc801kqSLL75Y69ev11NPPZUItrVR74bdddddevPNN7Vs2TIVFxd/5/6oOc5UXIqMs1pOTo7Kysq0aNGipOmLFi3S9773vbTLmVnSpWy1DRs2TMuWLVMkEklMe++991RUVKSePXsm2tRe73vvvafBgwcrOzv7JPbm9NdQvXNycnTZZZfV+TqDTz/9VCUlJWn7pd6pNeX4LigoUOfOnbVt2zatXbtW48aNS9sv9T551aF227Ztev/99xv1BpF6N5+DBw9qz5496t69e9o21PvkuK4r13UVCiW/tQyHw4mrcVKh3umZme68804tWLBAixcvVq9evZqlX2qOM1ZGb1UFnIaqvw5l7ty5tmnTJrvnnnusTZs2tmvXLjt69KjNmDHDVq5cabt27bJ169bZlClTLDc3N+lOsg8++KDdeuuticeHDh2yrl272k033WQbNmywBQsWWH5+fspb6d977722adMmmzt37llxK/366m1mtmDBAsvOzrYXXnjBtm3bZn/5y18sHA7b8uXLE31Q78ZrqN7//Oc/rby83Hbs2GELFy60kpISmzBhQlIf1Lvxvv32W6uoqLCKigqTZM8884xVVFTY559/bq7r2nXXXWfFxcW2fv36pK+gOX78eKIP6t149dX722+/tfvuu89WrFhhO3futPLychs2bJidc845duTIkUQf1Lvx6qu3mdmIESOsf//+Vl5ebp999pnNmzfP8vLybPbs2Yk+qHfj/frXv7aCggJbsmRJ0vni2LFjiTYHDx60iooKe/vtt02Svfrqq1ZRUWH79u1LtKHmOFsQbAEze/bZZ62kpMRycnLs0ksvTdxKv7Ky0q6//norKiqynJwc6969u1133XW2evXqpOUnTZpkI0aMSJr2ySef2PDhwy03N9e6detmM2fOTNxGv9qSJUts0KBBlpOTYz179rTnnnvulO7n6SJdvavNnTvXevfubXl5eXbJJZfU+Q5E6t009dX7z3/+sxUXF1t2drade+659vDDDyeFLDPq3RTl5eUmqc7PpEmTEl/HkeqnvLw80Qf1brz66n3s2DEbPXq0de7cOXF8T5o0yXbv3p3UB/VuvPrqbWa2b98+mzx5shUVFVleXp716dPHnn766aTaUe/GS3e+mDdvXqLNvHnzUrZ55JFHEm2oOc4Wjln8L8EBAAAAAAgg/sYWAAAAABBoBFsAAAAAQKARbAEAAAAAgUawBQAAAAAEGsEWAAAAABBoBFsAAAAAQKARbAEAAAAAgUawBQAgjZkzZ2rgwIEZX++SJUvkOI4cx9H48eMzvv5qPXv2TGzHoUOHWmw7AABoCMEWAHBWqg5s6X4mT56s+++/Xx988EGLbePWrVv10ksvJR5fddVVuueee+q0W7hwoRzHSbSpb7969uwpSdq/f7/uuusunXfeecrNzVWPHj107bXXJu3vmjVrNH/+/FO5iwAANIuslt4AAABawr59+xL/f+211/T73/9eW7duTUxr1aqV2rZtq7Zt27bE5kmSunTpovbt2zdpmQULFigSiUiS9uzZo8svv1zvv/+++vfvL0kKh8PatWuXrrjiCrVv315//OMfdfHFF8t1Xb377ru64447tGXLFklS586dVVhY2Kz7BADAqcCILQDgrNStW7fET0FBgRzHqTOt9qXIkydP1vjx4/X444+ra9euat++vR599FF5nqcHHnhAhYWFKi4u1osvvpi0ri+//FITJ05Uhw4d1LFjR40bN067du06JftVWFiY2IfOnTtLkjp27Jg0bdq0aXIcR6tXr9aPf/xjXXjhherfv7+mT5+ujz766JRsFwAApxLBFgCAJli8eLH27t2rZcuW6ZlnntHMmTP1wx/+UB06dNCqVas0depUTZ06VXv27JEkHTt2TCNHjlTbtm21bNkyffjhh2rbtq3Gjh2bGFnNpP/+97/617/+pTvuuENt2rSpM7+pI8QAAJwOCLYAADRBYWGhZs2apT59+uj2229Xnz59dOzYMT300EO64IILNGPGDOXk5Ojf//63JOnVV19VKBTS3/72N1100UXq27ev5s2bp927d2vJkiUZ3/7t27fLzFRaWprxdQMAcKrwN7YAADRB//79FQqd+Fy4a9euGjBgQOJxOBxWx44ddeDAAUnSunXrtH37drVr1y6pn6qqKu3YsSMzG12DmUlS4mZTAACcCQi2AAA0QXZ2dtJjx3FSTvN9X5Lk+77Kysr08ssv1+mr+m9gGys/P1+HDx+uM/3QoUPKz89vVB8XXHCBHMfR5s2bW/SrhAAAaE5cigwAwCl06aWXatu2berSpYt69+6d9FNQUNCkvkpLS7V27do609esWaM+ffo0qo/CwkKNGTNGzz77rP73v//Vmc/31QIAgohgCwDAKXTLLbeoU6dOGjdunJYvX66dO3dq6dKluvvuu/XFF180qa9p06Zpx44duuOOO/Sf//xHn376qZ599lnNnTtXDzzwQKP7mT17tqLRqC6//HLNnz9f27Zt0+bNmzVr1iwNGzasqbsIAECLI9gCAHAKtW7dWsuWLdO5556rCRMmqG/fvrr99ttVWVnZ6MuHq/Xs2VPLly/Xjh07NHr0aF122WV66aWX9NJLL+knP/lJo/vp1auXPv74Y40cOVL33XefBgwYoB/84Af64IMP9NxzzzV1FwEAaHGOVd9FAgAAnBaWLFmikSNH6ptvvmnxr985nbYFAIB0GLEFAOA0VVxcrJtuuqnF1t+/f39dffXVLbZ+AAAaixFbAABOM5WVlfryyy8lSW3btlW3bt1aZDs+//xzua4rSTrvvPOSvuYIAIDTCcEWAAAAABBofPQKAAAAAAg0gi0AAAAAINAItgAAAACAQCPYAgAAAAACjWALAAAAAAg0gi0AAAAAINAItgAAAACAQCPYAgAAAAACjWALAAAAAAg0gi0AAAAAINAItgAAAACAQCPYAgAAAAACjWALAAAAAAg0gi0AAAAAINAItgAAAACAQCPYAgAAAAACjWALAAAAAAg0gi0AAAAAINAItgAAAACAQCPYAgAAAAACjWALAAAAAAg0gi0AAAAAINAItgAAAACAQCPYAgAAAAACjWALAAAAAAg0gi0AAAAAINAItgAAAACAQCPYAgAAAAACjWALAAAAAAg0gi0AAAAAINAItgAAAACAQCPYAgAAAAACjWALAAAAAAg0gi0AAAAAINAItgAAAACAQCPYAgAAAAACjWALAAAAAAg0gi0AAAAAINAItgAAAACAQCPYAgAAAAACjWALAAAAAAg0gi0AAAAAINAItgAAAACAQCPYAgAAAAACjWALAAAAAAg0gi0AAAAAINAItgAAAACAQCPYAgAAAAACjWALAAAAAAg0gi0AAAAAINAItgAAAACAQCPYAgAAAAACjWALAAAAAAg0gi0AAAAAINAItgAAAACAQCPYAgAAAAACjWALAAAAAAg0gi0AAAAAINAItgAAAACAQCPYAgAAAAACjWALAAAAAAg0gi0AAAAAINAItgAAAACAQCPYAgAAAAACjWALAAAAAAg0gi0AAAAAINAItgAAAACAQCPYAgAAAAACjWALAAAAAAg0gi0AAAAAINAItgAAAACAQCPYAgAAAAACjWALAAAAAAg0gi0AAAAAINAItgAAAACAQCPYAgAAAAACjWALAAAAAAg0gi0AAAAAINAItgAAAACAQCPYAgAAAAACjWALAAAAAAg0gi0AAAAAINAItgAAAACAQCPYAgAAAAACjWALAAAAAAg0gi0AAAAAINAItgAAAACAQCPYAgAAAAAC7f8DnpMhZJ59Xa0AAAAASUVORK5CYII=", + "text/html": [ + "\n", + "
\n", + "
\n", + " Figure\n", + "
\n", + " \n", + "
\n", + " " + ], + "text/plain": [ + "Canvas(toolbar=Toolbar(toolitems=[('Home', 'Reset original view', 'home', 'home'), ('Back', 'Back to previous …" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "ec47d67b4aed4584b5a963f8c19161df", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "AppLayout(children=(Dropdown(description='Field:', index=1, layout=Layout(grid_area='header', margin='0px 30% …" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'backscatter'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/MPLNOR/MPLNOR_tutorial.ipynb b/VAPs/quicklook/MPLNOR/MPLNOR_tutorial.ipynb new file mode 100644 index 00000000..13ff4d43 --- /dev/null +++ b/VAPs/quicklook/MPLNOR/MPLNOR_tutorial.ipynb @@ -0,0 +1,838 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# MPLNOR1CAMP.C1 Notebook\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/mplnor) for more information about this vap." + ] + }, + { + "cell_type": "markdown", + "id": "97097763", + "metadata": {}, + "source": [ + "In this notebook, we demonstrate the workflow to explore ARM vap data (using mplnor1camp as an example.) Value-added products (VAPs) are higher-order data products that have been analyzed and processed to ease scientists’ use of ARM data in atmospheric research and global climate models. \n", + "Here is the main content we will cover." + ] + }, + { + "cell_type": "markdown", + "id": "eddec40f", + "metadata": {}, + "source": [ + "# Table of Content\n", + "## Access the data\n", + "* How to retrieve the data\n", + "* Data path and file name conventions\n", + "* Load data\n", + "## Explore the data\n", + "* NetCDF Data structure\n", + "* Xarray essentials\n", + "* Xarray Variable\n", + "## Plot the data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "import random\n", + "\n", + "import glob\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "2372d5be", + "metadata": {}, + "source": [ + "## Access the data" + ] + }, + { + "cell_type": "markdown", + "id": "5b0d9684", + "metadata": {}, + "source": [ + "### How to retrieve the data\n", + "We assume the path \"/data/archive\" is available where you are running this notebook. You can use `os.path.exists(\"/data/archive\")` to verify if the path exists at your machine.\n", + "\n", + "\n", + "### Data path and file name conventions\n", + "There several common terminologies regarding ARM data, for example, data-stream-name, data-level, site, facility, instrument, etc. (For more details, please see [ARM Data File Standards Version 1.2](https://www.google.com/search?q=arm+datastream+facility+cite+definition&rlz=1C1GCEJ_enUS1029US1029&ei=hb41ZICrBPukqtsPvMODIA&ved=0ahUKEwjAgeiS0aL-AhV7kmoFHbzhAAQQ4dUDCBA&uact=5&oq=arm+datastream+facility+cite+definition&gs_lcp=Cgxnd3Mtd2l6LXNlcnAQAzIFCAAQogQyBQgAEKIEMgUIABCiBDIFCAAQogQyBQgAEKIEOgoIABBHENYEELADOgoIIRCgARDDBBAKSgQIQRgAUJ4DWIoOYO0PaAFwAXgAgAGMAYgBuQmSAQMyLjmYAQCgAQHIAQjAAQE&sclient=gws-wiz-serp).) \n", + "\n", + "For example, this notebook is called `mplnor1camp.c1`, where `mplnor1camp` is the \"datastream name\", and the `{process.ds_class_level}}` is the \"data level\".\n", + "\n", + "This datastream also contains site `sgp` and facility `C1`. (Note: individual datastream might have multiple site-facility pairs.)\n", + "In such a case, the data of this data-stream is stored at `/data/archive/sgp/sgpmplnor1campC1.c1`, which is in the format of `//.`. We can use the following method to assign the data-stream directory `datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )`\n", + "\n", + "The data files under datastream_dir also follows naming conventions. But once reach the datastream_dir level, the most import file naming convention to differentiate the files is \"yyyyMMdd.hhmmss\", which comes handy to filter out files based on datetime. For example, we can use `glob.glob(f'{datastream_dir}/*.200709*.*')` to filter files in 2007 September.\n", + "\n", + "(Note: refer to the https://adc.arm.gov/discovery/#/ and https://adc.arm.gov/solr8/metadata/select API to explore ARM datastream and assoicated available sites and facilities\n", + "\n", + "Please see the following examples in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d7e9eb85", + "metadata": {}, + "outputs": [], + "source": [ + "# Verify if DATA_DIR path exists\n", + "DATA_DIR = \"/data/archive\"\n", + "os.path.exists(DATA_DIR)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "586993fd", + "metadata": {}, + "outputs": [], + "source": [ + "# Speicify datastream_dir following the path conventions and check its existence\n", + "DATASTREAM_NAME = \"mplnor1camp\"\n", + "DATA_LEVEL = \"c1\"\n", + "site = \"sgp\"\n", + "facility = \"C1\"\n", + "datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )\n", + "print(datastream_dir)\n", + "print(os.path.exists(datastream_dir))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0742f7c1", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: list 5 (random) files under datastream_dir\n", + "files = os.listdir(datastream_dir)\n", + "files[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39b98a36", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: get most recent file\n", + "list_of_files = glob.glob(f\"{datastream_dir}/*\") # * means all if need specific format then *.csv\n", + "latest_file = max(list_of_files, key=os.path.getctime)\n", + "latest_file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "902d514e", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus sort datastream files based on datetime\n", + "files = os.listdir(datastream_dir)\n", + "file_sorted = files.copy()\n", + "file_sorted.sort() \n", + "print(file_sorted[:5])\n", + "\n", + "# to reverse\n", + "file_sorted_reverse = files.copy() \n", + "file_sorted_reverse.sort(reverse=True)\n", + "print(file_sorted_reverse[:5])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec5923b2", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: pattern matching\n", + "# filter the 200709** files under datastream_dir\n", + "files_filter = glob.glob(f'{datastream_dir}/*.200709*.*')\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c4a4aa18", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: use armnotebook_utils.file_filter (TODO) to filter files based on datastream info \n" + ] + }, + { + "cell_type": "markdown", + "id": "cfeb9efc", + "metadata": {}, + "source": [ + "### Load data\n", + "The arm data is stored in [NetCDF](https://en.wikipedia.org/wiki/NetCDF#:~:text=%22NetCDF%20(network%20Common%20Data%20Form,format%20for%20representing%20scientific%20data.) format. We can use xarray's `open_dataset` method to load single file, or `open_mfdataset` to open multiple files. (Note: the latter will still return a single xarray dataset object by combining multiple files.)\n", + "\n", + "Note: open_dataset keeps the file handle open and lazy loads its contents. All parameters are passed directly to open_dataset. It is a preferable method over load_dataset for memory-effiency. (more details, please see [xarray.open_dataset](https://docs.xarray.dev/en/stable/generated/xarray.open_dataset.html))\n", + "\n", + "See the following example in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a440a329", + "metadata": {}, + "outputs": [], + "source": [ + "# open single file\n", + "full_path = latest_file\n", + "print(full_path)\n", + "ds_single = xr.open_dataset(full_path)\n", + "ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0143a3d", + "metadata": {}, + "outputs": [], + "source": [ + "print(ds_single)\n", + "print(type(ds_single))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c0f8939", + "metadata": {}, + "outputs": [], + "source": [ + "# open multiple files\n", + "n_files = 3\n", + "full_paths = [os.path.join(datastream_dir, f_path) for f_path in file_sorted_reverse[:n_files]]\n", + "print(full_paths)\n", + "try: # Note: sometimes multiple files cannot be merged, so we used try except here.\n", + " ds_mutiple = xr.open_mfdataset(full_paths)\n", + " # ds_mutiple\n", + " print(type(ds_mutiple))\n", + " print(ds_mutiple)\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "3fa59943", + "metadata": {}, + "source": [ + "## Explore the data" + ] + }, + { + "cell_type": "markdown", + "id": "571f69b9", + "metadata": {}, + "source": [ + "### NetCDF Data structure \n", + "\n", + "\n", + "(If you are confident with NetCDF basics and xarray essentials, feel free to skip this session.)\n", + "Before we dive into data exploration, there are some eseential concpets we should be familiar with (shown below): \n", + "* Dataset\n", + "* Data array\n", + "* Variable\n", + "* Dimenssion\n", + "* Coordinate\n", + "* Data Type\n", + "* Meta Data (Attributes)\n", + "\n", + "We will not go into details about NetCDF basics and here are some references you might find helpful\n", + "* [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#)\n", + "* [Network Common Data Form (NetCDF)](https://www.unidata.ucar.edu/software/netcdf/) and [A Brief History of (netCDF) Time](https://www.unidata.ucar.edu/software/netcdf/time/recs.html)\n", + "\n", + "\n", + "\n", + "### Xarray essentials\n", + "Earlier we introduced the xarray pacakge and used `open_dataset` and `open_mfdataset` to retrieve NetCDF data file as an xarray Dataset object. Recall that you can review the data in a notebook by using `print(ds)` or `ds`. \n", + "\n", + "Using xarray to retrieve the aforementioned NetCDF basics is straightforwared. In general,\n", + "* Dataset: `ds`\n", + "* Data array: `ds.variables`\n", + "* Variable: `ds.ds.variables`\n", + "* Dimenssion: `ds.dims`\n", + "* Coordinate: `ds.coords`\n", + "* Data Type: `type`\n", + "* Meta Data (Attributes)\n", + "\n", + "Also, here are some references if you are new to xarray\n", + "* [Xarray in 45 minutes](https://tutorial.xarray.dev/overview/xarray-in-45-min.html)\n", + "* [Handling NetCDF Files using XArray for Absolute Beginners](https://towardsdatascience.com/handling-netcdf-files-using-xarray-for-absolute-beginners-111a8ab4463f)\n", + "\n", + "Try the following commands in action\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b3eac323", + "metadata": {}, + "outputs": [], + "source": [ + "ds = ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77ecf85d", + "metadata": {}, + "outputs": [], + "source": [ + "# Dataset \n", + "ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25e7de09", + "metadata": {}, + "outputs": [], + "source": [ + "# Data array, Variable\n", + "# Note: the info can be overwhelming. Do not attempt to grasp everything at the first glance.\n", + "ds.variables" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c41b67e", + "metadata": {}, + "outputs": [], + "source": [ + "# dimenssions\n", + "ds.dims" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "156f1dfc", + "metadata": {}, + "outputs": [], + "source": [ + "# coordinates\n", + "ds.coords" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "277d6064", + "metadata": {}, + "outputs": [], + "source": [ + "# Meta Data (Attributes)\n", + "ds.attrs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d334681f", + "metadata": {}, + "outputs": [], + "source": [ + "# type\n", + "print(type(ds))\n", + "print(type(ds.variables))\n", + "print(type(ds.dims))\n", + "print(type(ds.coords))\n", + "print(type(ds.attrs))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "643399d6", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: retrieve variable names only\n", + "list(ds.variables)" + ] + }, + { + "cell_type": "markdown", + "id": "1cf36878", + "metadata": {}, + "source": [ + "#### Discussion: variable vs. coordinates vs. dimenssions. \n", + "From [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#):\n", + "\n", + "* Dimession: A dimension may be used to represent a real physical dimension, for example, time, latitude, longitude, or height. A dimension might also be used to index other quantities, for example station or model-run-number.\n", + "A netCDF dimension has both a name and a length. A dimension length is an arbitrary positive integer, except that one dimension in a netCDF dataset can have the length UNLIMITED.\n", + "\n", + "* Variables: Variables are used to store the bulk of the data in a netCDF dataset. A variable represents an array of values of the same type. A scalar value is treated as a 0-dimensional array. A variable has a name, a data type, and a shape described by its list of dimensions specified when the variable is created. A variable may also have associated attributes, which may be added, deleted or changed after the variable is created.\n", + "\n", + "* Coordinate (Variables): It is legal for a variable to have the same name as a dimension. Such variables have no special meaning to the netCDF library. However there is a convention that such variables should be treated in a special way by software using this library.\n", + "A variable with the same name as a dimension is called a coordinate variable. It typically defines a physical coordinate corresponding to that dimension.\n", + "\n", + "\n", + "Tips: By definition, coordinates and dimenssions are also variable. For example ds.time is a coordinate but it is also a special variable. But in practice, when we talk about variable, it implies regular/non-coordinate variable. We will use this convention for the remaining notebook. " + ] + }, + { + "cell_type": "markdown", + "id": "d47120f9", + "metadata": {}, + "source": [ + "### Xarray Variable\n", + "\n", + "\n", + "We can use `ds.variables` to access varialbes of a dataset. But many times viewing print-out the whole datasets can be overwhemling. Instead, we would work on the individual variable (also called Xarray data array.) using the `ds[var_name]` syntax, where `var_name` is the name of the variable. See the following examples in action.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5a57e136", + "metadata": {}, + "outputs": [], + "source": [ + "print(type(ds[\"time\"]))\n", + "ds[\"time\"]" + ] + }, + { + "cell_type": "markdown", + "id": "51ec643e", + "metadata": {}, + "source": [ + "#### Variable properties\n", + "For individual varible, the following properties are mostly used with the assocaited xarray method to retrieve them. (assuming var = ds[var_name])\n", + "* name: `var.name`\n", + "* data content: `var.data`\n", + "* attributes: `var.attrs`\n", + "* dimenstions: `var.dims`\n", + "* data type: `var.data.dtype`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a632a525", + "metadata": {}, + "outputs": [], + "source": [ + "var_name = \"time\"\n", + "var = ds[var_name]\n", + "\n", + "print(\"var.name: \\n\", var.name, \"\\n\")\n", + "print(\"var.data: \\n\", var.data, \"\\n\")\n", + "print(\"var.attrs: \\n\", var.attrs, \"\\n\")\n", + "print(\"var.dims: \\n\", var.dims, \"\\n\")\n", + "print(\"var.data.dtype: \\n\", var.data.dtype, \"\\n\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b256b07f", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: indivdual variable info table (collect and display variable info as a tabular format)\n", + "df_info = pd.DataFrame()\n", + "df_info[\"var_name\"] = list(ds.variables)\n", + "df_info[\"dims\"] = df_info.var_name.apply(lambda x: ds[x].dims)\n", + "df_info[\"is_dim\"] = df_info.var_name.apply(lambda x: x in ds.dims)\n", + "df_info[\"n_dim\"] = df_info.var_name.apply(lambda x: len(ds[x].dims))\n", + "df_info[\"attrs\"] = df_info.var_name.apply(lambda x: ds[x].attrs)\n", + "df_info[\"dtype\"] = df_info.var_name.apply(lambda x: ds[x].data.dtype)\n", + "df_info" + ] + }, + { + "cell_type": "markdown", + "id": "e6a36b96", + "metadata": {}, + "source": [ + "### Data cleaning/Preprocessing (skipped)\n", + "Data cleaning and preprocessing is an important stage in the the data analysis pipeline. However it will be out of the scope of this notebook. For more information about data cleaning and preprocessing basics, here are some references. \n", + "* [Xarray Fundamentals](https://earth-env-data-science.github.io/lectures/xarray/xarray.html)\n", + "* [Xarray Tutorial — Pangeo Gallery documentation](http://gallery.pangeo.io/repos/pangeo-data/pangeo-tutorial-gallery/xarray.html)\n", + "* [Pythonic Data Cleaning With pandas and NumPy](https://realpython.com/python-data-cleaning-numpy-pandas/)\n", + "* [Pandas - Cleaning Data](https://www.w3schools.com/python/pandas/pandas_cleaning.asp)" + ] + }, + { + "cell_type": "markdown", + "id": "ae6e1a69", + "metadata": {}, + "source": [ + "### Plotting\n", + "Data visualization (or plotting) is another important data analysis topic and deserves its own discussion. Here in this notebook we will only demonstrate basic tool for simple data visualization tasks.\n", + "\n", + "Here are some reference you might find useful:\n", + "* xarray plotting: https://docs.xarray.dev/en/stable/user-guide/plotting.html\n", + "* Atmospheric Community Toolkit (ACT): https://arm-doe.github.io/ACT/index.html\n", + "\n", + "Note: this notebook is auto-generated using a template. It uses a general idea to select variable(s) to plot and is not customized to each indivdual notebook. Feel free to change the variables in intrrests, especially certain figure is failed to plot." + ] + }, + { + "cell_type": "markdown", + "id": "7e417fb2", + "metadata": {}, + "source": [ + "#### 1-dimenssional basic time series plot\n", + "\n", + "For the following plot we would like to find variables such that\n", + "* it has one and only one dimession\n", + "* \"time\" is its coordinate variable\n", + "* it is not a dimenssion itself,\n", + "* it is not a special variable with substrings within [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "\n", + "Please see the following example in action. (For more details about pandas filtering, please see the following references.)\n", + "* [pandas: multiple conditions while indexing data frame](https://stackoverflow.com/questions/22591174/pandas-multiple-conditions-while-indexing-data-frame-unexpected-behavior)\n", + "* [pandas.DataFrame.apply](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.apply.html)\n", + "* [How to test if a string contains one of the substrings in a list...](https://stackoverflow.com/questions/26577516/how-to-test-if-a-string-contains-one-of-the-substrings-in-a-list-in-pandas" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30edac2d", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==1, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter = df_info[(df_info.n_dim == 1) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4777a659", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_1d = df_filter.var_name.values[0]\n", + " var_1d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb5ae985", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " ds[var_1d].plot()\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "9e41066e", + "metadata": {}, + "source": [ + "#### 2-dimenssional basic plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09bd4adc", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==2, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_2 = df_info[(df_info.n_dim == 2) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2cdea1a9", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_2d = df_filter_2.var_name.values[0]\n", + " var_2d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5f48879", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " print(ds[var_2d].dims)\n", + " # ds[var_2d].plot()\n", + "\n", + " # conventionally, use \"time\" as x-axis\n", + " ds[var_2d].plot(x=\"time\")\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "7554b2a6", + "metadata": {}, + "source": [ + "#### qc-plotting (optional)\n", + "\n", + "Note: act qc plotting has more strict requirement, one of them is the associated qc variable need to have \"flag_masks\" attributes. Which is added by using ds = act.io.armfiles.read_netcdf(files_list), then ds.clean.cleanup()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71c2096f", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " ds_act = act.io.armfiles.read_netcdf(full_path)\n", + " print(type(ds_act))\n", + " ds_act.clean.cleanup()\n", + "\n", + " # or \n", + " # ds.clean.cleanup()\n", + "except Exception as e:\n", + " print(\"ERROR\", e)\n", + " ds_act = ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "844f8505", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter valid variables for ACT qc plotting\n", + "condition = (\"qc_\" + df_info.var_name).apply(lambda x: ds_act[x].attrs.get(\"flag_masks\") is not None \n", + " if x in list(ds.variables) else False)\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_3 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings))) &\n", + " condition\n", + " ]\n", + "df_filter_3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd38cc6", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "try:\n", + " qc_variable = df_filter_3.var_name.values[0]\n", + " print(qc_variable)\n", + "except Exception as e:\n", + " print(e)\n", + " \n", + "try:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + " qc_ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "d76e4d27", + "metadata": {}, + "source": [ + "#### bonus: choose variables to plot from a dropdown menu " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb733804", + "metadata": {}, + "outputs": [], + "source": [ + "# Valid variables filtering\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_4 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82d5c20d", + "metadata": {}, + "outputs": [], + "source": [ + "# example 1: using xarray plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2fc9aaa4", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# fig, ax = plt.subplots(figsize=(10, 4))\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "# fig.clear() # Remove old lines from plot and plot new one\n", + "# if len(ds[var].dims)==2:\n", + "# ds[var].plot(x=\"time\", add_colorbar=False)\n", + "# else:\n", + "# ds[var].plot()\n", + "# plt.grid()\n", + "# plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fabdd802", + "metadata": {}, + "outputs": [], + "source": [ + "# example 2: using act plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "962b4186", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "\n", + "\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "\n", + "# i_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + "# i_display.add_subplots((1,), figsize=(10, 4))\n", + "# ax = i_display.plot(var, subplot_index=(0,), set_title=f\"{var} ({ds_act[var].attrs['long_name']})\",)\n", + "\n", + "# ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "# ax.grid()\n", + "# plt.show()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/MPLNOR/mplnor1camp.c1.ipynb b/VAPs/quicklook/MPLNOR/mplnor1camp.c1.ipynb new file mode 100644 index 00000000..ebfd1041 --- /dev/null +++ b/VAPs/quicklook/MPLNOR/mplnor1camp.c1.ipynb @@ -0,0 +1,297 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# MPLNOR1CAMP.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/mplnor) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'mplnor1camp'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2004-05-11', 'facility': 'C1', 'site': 'sgp', 'start_date': '1996-05-01'}, {'end_date': '1999-11-18', 'facility': 'C2', 'site': 'twp', 'start_date': '1998-11-20'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2004-05-10'\n", + "date_end = '2004-05-11'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['backscatter', 'cloud_base_height', 'cloud_top_height']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'backscatter'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/MWRRET/MWRRET_tutorial.ipynb b/VAPs/quicklook/MWRRET/MWRRET_tutorial.ipynb new file mode 100644 index 00000000..735e20d6 --- /dev/null +++ b/VAPs/quicklook/MWRRET/MWRRET_tutorial.ipynb @@ -0,0 +1,838 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# MWRRET1LILJCLOU.C1 Notebook\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/mwrret) for more information about this vap." + ] + }, + { + "cell_type": "markdown", + "id": "97097763", + "metadata": {}, + "source": [ + "In this notebook, we demonstrate the workflow to explore ARM vap data (using mwrret1liljclou as an example.) Value-added products (VAPs) are higher-order data products that have been analyzed and processed to ease scientists’ use of ARM data in atmospheric research and global climate models. \n", + "Here is the main content we will cover." + ] + }, + { + "cell_type": "markdown", + "id": "eddec40f", + "metadata": {}, + "source": [ + "# Table of Content\n", + "## Access the data\n", + "* How to retrieve the data\n", + "* Data path and file name conventions\n", + "* Load data\n", + "## Explore the data\n", + "* NetCDF Data structure\n", + "* Xarray essentials\n", + "* Xarray Variable\n", + "## Plot the data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "import random\n", + "\n", + "import glob\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "2372d5be", + "metadata": {}, + "source": [ + "## Access the data" + ] + }, + { + "cell_type": "markdown", + "id": "5b0d9684", + "metadata": {}, + "source": [ + "### How to retrieve the data\n", + "We assume the path \"/data/archive\" is available where you are running this notebook. You can use `os.path.exists(\"/data/archive\")` to verify if the path exists at your machine.\n", + "\n", + "\n", + "### Data path and file name conventions\n", + "There several common terminologies regarding ARM data, for example, data-stream-name, data-level, site, facility, instrument, etc. (For more details, please see [ARM Data File Standards Version 1.2](https://www.google.com/search?q=arm+datastream+facility+cite+definition&rlz=1C1GCEJ_enUS1029US1029&ei=hb41ZICrBPukqtsPvMODIA&ved=0ahUKEwjAgeiS0aL-AhV7kmoFHbzhAAQQ4dUDCBA&uact=5&oq=arm+datastream+facility+cite+definition&gs_lcp=Cgxnd3Mtd2l6LXNlcnAQAzIFCAAQogQyBQgAEKIEMgUIABCiBDIFCAAQogQyBQgAEKIEOgoIABBHENYEELADOgoIIRCgARDDBBAKSgQIQRgAUJ4DWIoOYO0PaAFwAXgAgAGMAYgBuQmSAQMyLjmYAQCgAQHIAQjAAQE&sclient=gws-wiz-serp).) \n", + "\n", + "For example, this notebook is called `mwrret1liljclou.c1`, where `mwrret1liljclou` is the \"datastream name\", and the `{process.ds_class_level}}` is the \"data level\".\n", + "\n", + "This datastream also contains site `asi` and facility `M1`. (Note: individual datastream might have multiple site-facility pairs.)\n", + "In such a case, the data of this data-stream is stored at `/data/archive/asi/asimwrret1liljclouM1.c1`, which is in the format of `//.`. We can use the following method to assign the data-stream directory `datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )`\n", + "\n", + "The data files under datastream_dir also follows naming conventions. But once reach the datastream_dir level, the most import file naming convention to differentiate the files is \"yyyyMMdd.hhmmss\", which comes handy to filter out files based on datetime. For example, we can use `glob.glob(f'{datastream_dir}/*.200709*.*')` to filter files in 2007 September.\n", + "\n", + "(Note: refer to the https://adc.arm.gov/discovery/#/ and https://adc.arm.gov/solr8/metadata/select API to explore ARM datastream and assoicated available sites and facilities\n", + "\n", + "Please see the following examples in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d7e9eb85", + "metadata": {}, + "outputs": [], + "source": [ + "# Verify if DATA_DIR path exists\n", + "DATA_DIR = \"/data/archive\"\n", + "os.path.exists(DATA_DIR)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "586993fd", + "metadata": {}, + "outputs": [], + "source": [ + "# Speicify datastream_dir following the path conventions and check its existence\n", + "DATASTREAM_NAME = \"mwrret1liljclou\"\n", + "DATA_LEVEL = \"c1\"\n", + "site = \"asi\"\n", + "facility = \"M1\"\n", + "datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )\n", + "print(datastream_dir)\n", + "print(os.path.exists(datastream_dir))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0742f7c1", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: list 5 (random) files under datastream_dir\n", + "files = os.listdir(datastream_dir)\n", + "files[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39b98a36", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: get most recent file\n", + "list_of_files = glob.glob(f\"{datastream_dir}/*\") # * means all if need specific format then *.csv\n", + "latest_file = max(list_of_files, key=os.path.getctime)\n", + "latest_file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "902d514e", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus sort datastream files based on datetime\n", + "files = os.listdir(datastream_dir)\n", + "file_sorted = files.copy()\n", + "file_sorted.sort() \n", + "print(file_sorted[:5])\n", + "\n", + "# to reverse\n", + "file_sorted_reverse = files.copy() \n", + "file_sorted_reverse.sort(reverse=True)\n", + "print(file_sorted_reverse[:5])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec5923b2", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: pattern matching\n", + "# filter the 200709** files under datastream_dir\n", + "files_filter = glob.glob(f'{datastream_dir}/*.200709*.*')\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c4a4aa18", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: use armnotebook_utils.file_filter (TODO) to filter files based on datastream info \n" + ] + }, + { + "cell_type": "markdown", + "id": "cfeb9efc", + "metadata": {}, + "source": [ + "### Load data\n", + "The arm data is stored in [NetCDF](https://en.wikipedia.org/wiki/NetCDF#:~:text=%22NetCDF%20(network%20Common%20Data%20Form,format%20for%20representing%20scientific%20data.) format. We can use xarray's `open_dataset` method to load single file, or `open_mfdataset` to open multiple files. (Note: the latter will still return a single xarray dataset object by combining multiple files.)\n", + "\n", + "Note: open_dataset keeps the file handle open and lazy loads its contents. All parameters are passed directly to open_dataset. It is a preferable method over load_dataset for memory-effiency. (more details, please see [xarray.open_dataset](https://docs.xarray.dev/en/stable/generated/xarray.open_dataset.html))\n", + "\n", + "See the following example in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a440a329", + "metadata": {}, + "outputs": [], + "source": [ + "# open single file\n", + "full_path = latest_file\n", + "print(full_path)\n", + "ds_single = xr.open_dataset(full_path)\n", + "ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0143a3d", + "metadata": {}, + "outputs": [], + "source": [ + "print(ds_single)\n", + "print(type(ds_single))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c0f8939", + "metadata": {}, + "outputs": [], + "source": [ + "# open multiple files\n", + "n_files = 3\n", + "full_paths = [os.path.join(datastream_dir, f_path) for f_path in file_sorted_reverse[:n_files]]\n", + "print(full_paths)\n", + "try: # Note: sometimes multiple files cannot be merged, so we used try except here.\n", + " ds_mutiple = xr.open_mfdataset(full_paths)\n", + " # ds_mutiple\n", + " print(type(ds_mutiple))\n", + " print(ds_mutiple)\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "3fa59943", + "metadata": {}, + "source": [ + "## Explore the data" + ] + }, + { + "cell_type": "markdown", + "id": "571f69b9", + "metadata": {}, + "source": [ + "### NetCDF Data structure \n", + "\n", + "\n", + "(If you are confident with NetCDF basics and xarray essentials, feel free to skip this session.)\n", + "Before we dive into data exploration, there are some eseential concpets we should be familiar with (shown below): \n", + "* Dataset\n", + "* Data array\n", + "* Variable\n", + "* Dimenssion\n", + "* Coordinate\n", + "* Data Type\n", + "* Meta Data (Attributes)\n", + "\n", + "We will not go into details about NetCDF basics and here are some references you might find helpful\n", + "* [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#)\n", + "* [Network Common Data Form (NetCDF)](https://www.unidata.ucar.edu/software/netcdf/) and [A Brief History of (netCDF) Time](https://www.unidata.ucar.edu/software/netcdf/time/recs.html)\n", + "\n", + "\n", + "\n", + "### Xarray essentials\n", + "Earlier we introduced the xarray pacakge and used `open_dataset` and `open_mfdataset` to retrieve NetCDF data file as an xarray Dataset object. Recall that you can review the data in a notebook by using `print(ds)` or `ds`. \n", + "\n", + "Using xarray to retrieve the aforementioned NetCDF basics is straightforwared. In general,\n", + "* Dataset: `ds`\n", + "* Data array: `ds.variables`\n", + "* Variable: `ds.ds.variables`\n", + "* Dimenssion: `ds.dims`\n", + "* Coordinate: `ds.coords`\n", + "* Data Type: `type`\n", + "* Meta Data (Attributes)\n", + "\n", + "Also, here are some references if you are new to xarray\n", + "* [Xarray in 45 minutes](https://tutorial.xarray.dev/overview/xarray-in-45-min.html)\n", + "* [Handling NetCDF Files using XArray for Absolute Beginners](https://towardsdatascience.com/handling-netcdf-files-using-xarray-for-absolute-beginners-111a8ab4463f)\n", + "\n", + "Try the following commands in action\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b3eac323", + "metadata": {}, + "outputs": [], + "source": [ + "ds = ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77ecf85d", + "metadata": {}, + "outputs": [], + "source": [ + "# Dataset \n", + "ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25e7de09", + "metadata": {}, + "outputs": [], + "source": [ + "# Data array, Variable\n", + "# Note: the info can be overwhelming. Do not attempt to grasp everything at the first glance.\n", + "ds.variables" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c41b67e", + "metadata": {}, + "outputs": [], + "source": [ + "# dimenssions\n", + "ds.dims" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "156f1dfc", + "metadata": {}, + "outputs": [], + "source": [ + "# coordinates\n", + "ds.coords" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "277d6064", + "metadata": {}, + "outputs": [], + "source": [ + "# Meta Data (Attributes)\n", + "ds.attrs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d334681f", + "metadata": {}, + "outputs": [], + "source": [ + "# type\n", + "print(type(ds))\n", + "print(type(ds.variables))\n", + "print(type(ds.dims))\n", + "print(type(ds.coords))\n", + "print(type(ds.attrs))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "643399d6", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: retrieve variable names only\n", + "list(ds.variables)" + ] + }, + { + "cell_type": "markdown", + "id": "1cf36878", + "metadata": {}, + "source": [ + "#### Discussion: variable vs. coordinates vs. dimenssions. \n", + "From [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#):\n", + "\n", + "* Dimession: A dimension may be used to represent a real physical dimension, for example, time, latitude, longitude, or height. A dimension might also be used to index other quantities, for example station or model-run-number.\n", + "A netCDF dimension has both a name and a length. A dimension length is an arbitrary positive integer, except that one dimension in a netCDF dataset can have the length UNLIMITED.\n", + "\n", + "* Variables: Variables are used to store the bulk of the data in a netCDF dataset. A variable represents an array of values of the same type. A scalar value is treated as a 0-dimensional array. A variable has a name, a data type, and a shape described by its list of dimensions specified when the variable is created. A variable may also have associated attributes, which may be added, deleted or changed after the variable is created.\n", + "\n", + "* Coordinate (Variables): It is legal for a variable to have the same name as a dimension. Such variables have no special meaning to the netCDF library. However there is a convention that such variables should be treated in a special way by software using this library.\n", + "A variable with the same name as a dimension is called a coordinate variable. It typically defines a physical coordinate corresponding to that dimension.\n", + "\n", + "\n", + "Tips: By definition, coordinates and dimenssions are also variable. For example ds.time is a coordinate but it is also a special variable. But in practice, when we talk about variable, it implies regular/non-coordinate variable. We will use this convention for the remaining notebook. " + ] + }, + { + "cell_type": "markdown", + "id": "d47120f9", + "metadata": {}, + "source": [ + "### Xarray Variable\n", + "\n", + "\n", + "We can use `ds.variables` to access varialbes of a dataset. But many times viewing print-out the whole datasets can be overwhemling. Instead, we would work on the individual variable (also called Xarray data array.) using the `ds[var_name]` syntax, where `var_name` is the name of the variable. See the following examples in action.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5a57e136", + "metadata": {}, + "outputs": [], + "source": [ + "print(type(ds[\"time\"]))\n", + "ds[\"time\"]" + ] + }, + { + "cell_type": "markdown", + "id": "51ec643e", + "metadata": {}, + "source": [ + "#### Variable properties\n", + "For individual varible, the following properties are mostly used with the assocaited xarray method to retrieve them. (assuming var = ds[var_name])\n", + "* name: `var.name`\n", + "* data content: `var.data`\n", + "* attributes: `var.attrs`\n", + "* dimenstions: `var.dims`\n", + "* data type: `var.data.dtype`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a632a525", + "metadata": {}, + "outputs": [], + "source": [ + "var_name = \"time\"\n", + "var = ds[var_name]\n", + "\n", + "print(\"var.name: \\n\", var.name, \"\\n\")\n", + "print(\"var.data: \\n\", var.data, \"\\n\")\n", + "print(\"var.attrs: \\n\", var.attrs, \"\\n\")\n", + "print(\"var.dims: \\n\", var.dims, \"\\n\")\n", + "print(\"var.data.dtype: \\n\", var.data.dtype, \"\\n\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b256b07f", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: indivdual variable info table (collect and display variable info as a tabular format)\n", + "df_info = pd.DataFrame()\n", + "df_info[\"var_name\"] = list(ds.variables)\n", + "df_info[\"dims\"] = df_info.var_name.apply(lambda x: ds[x].dims)\n", + "df_info[\"is_dim\"] = df_info.var_name.apply(lambda x: x in ds.dims)\n", + "df_info[\"n_dim\"] = df_info.var_name.apply(lambda x: len(ds[x].dims))\n", + "df_info[\"attrs\"] = df_info.var_name.apply(lambda x: ds[x].attrs)\n", + "df_info[\"dtype\"] = df_info.var_name.apply(lambda x: ds[x].data.dtype)\n", + "df_info" + ] + }, + { + "cell_type": "markdown", + "id": "e6a36b96", + "metadata": {}, + "source": [ + "### Data cleaning/Preprocessing (skipped)\n", + "Data cleaning and preprocessing is an important stage in the the data analysis pipeline. However it will be out of the scope of this notebook. For more information about data cleaning and preprocessing basics, here are some references. \n", + "* [Xarray Fundamentals](https://earth-env-data-science.github.io/lectures/xarray/xarray.html)\n", + "* [Xarray Tutorial — Pangeo Gallery documentation](http://gallery.pangeo.io/repos/pangeo-data/pangeo-tutorial-gallery/xarray.html)\n", + "* [Pythonic Data Cleaning With pandas and NumPy](https://realpython.com/python-data-cleaning-numpy-pandas/)\n", + "* [Pandas - Cleaning Data](https://www.w3schools.com/python/pandas/pandas_cleaning.asp)" + ] + }, + { + "cell_type": "markdown", + "id": "ae6e1a69", + "metadata": {}, + "source": [ + "### Plotting\n", + "Data visualization (or plotting) is another important data analysis topic and deserves its own discussion. Here in this notebook we will only demonstrate basic tool for simple data visualization tasks.\n", + "\n", + "Here are some reference you might find useful:\n", + "* xarray plotting: https://docs.xarray.dev/en/stable/user-guide/plotting.html\n", + "* Atmospheric Community Toolkit (ACT): https://arm-doe.github.io/ACT/index.html\n", + "\n", + "Note: this notebook is auto-generated using a template. It uses a general idea to select variable(s) to plot and is not customized to each indivdual notebook. Feel free to change the variables in intrrests, especially certain figure is failed to plot." + ] + }, + { + "cell_type": "markdown", + "id": "7e417fb2", + "metadata": {}, + "source": [ + "#### 1-dimenssional basic time series plot\n", + "\n", + "For the following plot we would like to find variables such that\n", + "* it has one and only one dimession\n", + "* \"time\" is its coordinate variable\n", + "* it is not a dimenssion itself,\n", + "* it is not a special variable with substrings within [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "\n", + "Please see the following example in action. (For more details about pandas filtering, please see the following references.)\n", + "* [pandas: multiple conditions while indexing data frame](https://stackoverflow.com/questions/22591174/pandas-multiple-conditions-while-indexing-data-frame-unexpected-behavior)\n", + "* [pandas.DataFrame.apply](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.apply.html)\n", + "* [How to test if a string contains one of the substrings in a list...](https://stackoverflow.com/questions/26577516/how-to-test-if-a-string-contains-one-of-the-substrings-in-a-list-in-pandas" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30edac2d", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==1, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter = df_info[(df_info.n_dim == 1) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4777a659", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_1d = df_filter.var_name.values[0]\n", + " var_1d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb5ae985", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " ds[var_1d].plot()\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "9e41066e", + "metadata": {}, + "source": [ + "#### 2-dimenssional basic plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09bd4adc", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==2, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_2 = df_info[(df_info.n_dim == 2) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2cdea1a9", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_2d = df_filter_2.var_name.values[0]\n", + " var_2d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5f48879", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " print(ds[var_2d].dims)\n", + " # ds[var_2d].plot()\n", + "\n", + " # conventionally, use \"time\" as x-axis\n", + " ds[var_2d].plot(x=\"time\")\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "7554b2a6", + "metadata": {}, + "source": [ + "#### qc-plotting (optional)\n", + "\n", + "Note: act qc plotting has more strict requirement, one of them is the associated qc variable need to have \"flag_masks\" attributes. Which is added by using ds = act.io.armfiles.read_netcdf(files_list), then ds.clean.cleanup()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71c2096f", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " ds_act = act.io.armfiles.read_netcdf(full_path)\n", + " print(type(ds_act))\n", + " ds_act.clean.cleanup()\n", + "\n", + " # or \n", + " # ds.clean.cleanup()\n", + "except Exception as e:\n", + " print(\"ERROR\", e)\n", + " ds_act = ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "844f8505", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter valid variables for ACT qc plotting\n", + "condition = (\"qc_\" + df_info.var_name).apply(lambda x: ds_act[x].attrs.get(\"flag_masks\") is not None \n", + " if x in list(ds.variables) else False)\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_3 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings))) &\n", + " condition\n", + " ]\n", + "df_filter_3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd38cc6", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "try:\n", + " qc_variable = df_filter_3.var_name.values[0]\n", + " print(qc_variable)\n", + "except Exception as e:\n", + " print(e)\n", + " \n", + "try:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + " qc_ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "d76e4d27", + "metadata": {}, + "source": [ + "#### bonus: choose variables to plot from a dropdown menu " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb733804", + "metadata": {}, + "outputs": [], + "source": [ + "# Valid variables filtering\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_4 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82d5c20d", + "metadata": {}, + "outputs": [], + "source": [ + "# example 1: using xarray plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2fc9aaa4", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# fig, ax = plt.subplots(figsize=(10, 4))\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "# fig.clear() # Remove old lines from plot and plot new one\n", + "# if len(ds[var].dims)==2:\n", + "# ds[var].plot(x=\"time\", add_colorbar=False)\n", + "# else:\n", + "# ds[var].plot()\n", + "# plt.grid()\n", + "# plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fabdd802", + "metadata": {}, + "outputs": [], + "source": [ + "# example 2: using act plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "962b4186", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "\n", + "\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "\n", + "# i_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + "# i_display.add_subplots((1,), figsize=(10, 4))\n", + "# ax = i_display.plot(var, subplot_index=(0,), set_title=f\"{var} ({ds_act[var].attrs['long_name']})\",)\n", + "\n", + "# ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "# ax.grid()\n", + "# plt.show()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/MWRRET/mwrret1liljclou.c1.ipynb b/VAPs/quicklook/MWRRET/mwrret1liljclou.c1.ipynb new file mode 100644 index 00000000..409a8fc3 --- /dev/null +++ b/VAPs/quicklook/MWRRET/mwrret1liljclou.c1.ipynb @@ -0,0 +1,339 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# MWRRET1LILJCLOU.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/mwrret) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'mwrret1liljclou'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2017-11-01', 'facility': 'M1', 'site': 'asi', 'start_date': '2016-05-21'}, {'end_date': '2017-10-31', 'facility': 'S1', 'site': 'asi', 'start_date': '2016-05-28'}, {'end_date': '2012-02-08', 'facility': 'M1', 'site': 'gan', 'start_date': '2011-10-05'}, {'end_date': '2023-12-14', 'facility': 'C1', 'site': 'nsa', 'start_date': '2011-01-01'}, {'end_date': '2023-12-14', 'facility': 'C1', 'site': 'sgp', 'start_date': '2011-01-01'}, {'end_date': '2014-05-19', 'facility': 'C1', 'site': 'twp', 'start_date': '2011-01-01'}, {'end_date': '2013-09-08', 'facility': 'C2', 'site': 'twp', 'start_date': '2009-02-15'}, {'end_date': '2015-01-04', 'facility': 'C3', 'site': 'twp', 'start_date': '2011-01-01'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2023-12-12'\n", + "date_end = '2023-12-14'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['be_pwv', 'be_lwp']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'be_pwv'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'be_pwv'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/MWRRET/mwrret1liljclou.c2.ipynb b/VAPs/quicklook/MWRRET/mwrret1liljclou.c2.ipynb new file mode 100644 index 00000000..182a4cd1 --- /dev/null +++ b/VAPs/quicklook/MWRRET/mwrret1liljclou.c2.ipynb @@ -0,0 +1,339 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# MWRRET1LILJCLOU.C2 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/mwrret) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'mwrret1liljclou'\n", + "DATA_LEVEL = 'c2'\n", + "LOCATIONS = [{'end_date': '2017-01-01', 'facility': 'M1', 'site': 'awr', 'start_date': '2016-01-30'}, {'end_date': '2016-01-17', 'facility': 'S1', 'site': 'awr', 'start_date': '2015-12-06'}, {'end_date': '2020-06-01', 'facility': 'M1', 'site': 'anx', 'start_date': '2019-12-01'}, {'end_date': '2023-06-16', 'facility': 'M1', 'site': 'guc', 'start_date': '2021-09-01'}, {'end_date': '2022-10-01', 'facility': 'M1', 'site': 'hou', 'start_date': '2021-08-28'}, {'end_date': '2008-01-01', 'facility': 'M1', 'site': 'fkb', 'start_date': '2007-03-14'}, {'end_date': '2015-12-01', 'facility': 'M1', 'site': 'mao', 'start_date': '2014-01-08'}, {'end_date': '2018-03-23', 'facility': 'M1', 'site': 'mar', 'start_date': '2017-10-21'}, {'end_date': '2018-03-09', 'facility': 'S1', 'site': 'mcq', 'start_date': '2016-12-10'}, {'end_date': '2008-12-24', 'facility': 'M1', 'site': 'hfe', 'start_date': '2008-05-09'}, {'end_date': '2019-05-01', 'facility': 'M1', 'site': 'cor', 'start_date': '2018-09-23'}, {'end_date': '2013-09-26', 'facility': 'M1', 'site': 'mag', 'start_date': '2012-10-05'}, {'end_date': '2011-01-06', 'facility': 'M1', 'site': 'grw', 'start_date': '2009-04-30'}, {'end_date': '2013-05-20', 'facility': 'M1', 'site': 'pvc', 'start_date': '2012-06-29'}, {'end_date': '2005-09-15', 'facility': 'M1', 'site': 'pye', 'start_date': '2005-02-01'}, {'end_date': '2011-04-25', 'facility': 'M1', 'site': 'sbs', 'start_date': '2010-09-27'}, {'end_date': '2020-10-01', 'facility': 'M1', 'site': 'mos', 'start_date': '2019-10-11'}, {'end_date': '2012-03-27', 'facility': 'M1', 'site': 'pgh', 'start_date': '2011-06-20'}, {'end_date': '2022-12-31', 'facility': 'C1', 'site': 'nsa', 'start_date': '2001-04-01'}, {'end_date': '2010-12-31', 'facility': 'C2', 'site': 'nsa', 'start_date': '2001-04-01'}, {'end_date': '2007-01-08', 'facility': 'M1', 'site': 'nim', 'start_date': '2005-11-28'}, {'end_date': '2009-10-16', 'facility': 'B1', 'site': 'sgp', 'start_date': '2001-04-01'}, {'end_date': '2009-04-20', 'facility': 'B4', 'site': 'sgp', 'start_date': '2001-04-01'}, {'end_date': '2009-04-21', 'facility': 'B5', 'site': 'sgp', 'start_date': '2001-04-01'}, {'end_date': '2009-01-29', 'facility': 'B6', 'site': 'sgp', 'start_date': '2001-04-01'}, {'end_date': '2021-12-31', 'facility': 'C1', 'site': 'sgp', 'start_date': '1996-09-01'}, {'end_date': '2014-05-18', 'facility': 'C1', 'site': 'twp', 'start_date': '1996-10-11'}, {'end_date': '2009-02-14', 'facility': 'C2', 'site': 'twp', 'start_date': '1998-10-28'}, {'end_date': '2013-12-31', 'facility': 'C3', 'site': 'twp', 'start_date': '2002-03-12'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'B1' )\n", + "\n", + "date_start = '2009-10-14'\n", + "date_end = '2009-10-16'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['be_pwv', 'be_lwp']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'be_pwv'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'be_pwv'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/MWRRETV2/MWRRETV2_tutorial.ipynb b/VAPs/quicklook/MWRRETV2/MWRRETV2_tutorial.ipynb new file mode 100644 index 00000000..9f78a297 --- /dev/null +++ b/VAPs/quicklook/MWRRETV2/MWRRETV2_tutorial.ipynb @@ -0,0 +1,838 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# MWRRET2TURN.C1 Notebook\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/mwrretv2) for more information about this vap." + ] + }, + { + "cell_type": "markdown", + "id": "97097763", + "metadata": {}, + "source": [ + "In this notebook, we demonstrate the workflow to explore ARM vap data (using mwrret2turn as an example.) Value-added products (VAPs) are higher-order data products that have been analyzed and processed to ease scientists’ use of ARM data in atmospheric research and global climate models. \n", + "Here is the main content we will cover." + ] + }, + { + "cell_type": "markdown", + "id": "eddec40f", + "metadata": {}, + "source": [ + "# Table of Content\n", + "## Access the data\n", + "* How to retrieve the data\n", + "* Data path and file name conventions\n", + "* Load data\n", + "## Explore the data\n", + "* NetCDF Data structure\n", + "* Xarray essentials\n", + "* Xarray Variable\n", + "## Plot the data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "import random\n", + "\n", + "import glob\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "2372d5be", + "metadata": {}, + "source": [ + "## Access the data" + ] + }, + { + "cell_type": "markdown", + "id": "5b0d9684", + "metadata": {}, + "source": [ + "### How to retrieve the data\n", + "We assume the path \"/data/archive\" is available where you are running this notebook. You can use `os.path.exists(\"/data/archive\")` to verify if the path exists at your machine.\n", + "\n", + "\n", + "### Data path and file name conventions\n", + "There several common terminologies regarding ARM data, for example, data-stream-name, data-level, site, facility, instrument, etc. (For more details, please see [ARM Data File Standards Version 1.2](https://www.google.com/search?q=arm+datastream+facility+cite+definition&rlz=1C1GCEJ_enUS1029US1029&ei=hb41ZICrBPukqtsPvMODIA&ved=0ahUKEwjAgeiS0aL-AhV7kmoFHbzhAAQQ4dUDCBA&uact=5&oq=arm+datastream+facility+cite+definition&gs_lcp=Cgxnd3Mtd2l6LXNlcnAQAzIFCAAQogQyBQgAEKIEMgUIABCiBDIFCAAQogQyBQgAEKIEOgoIABBHENYEELADOgoIIRCgARDDBBAKSgQIQRgAUJ4DWIoOYO0PaAFwAXgAgAGMAYgBuQmSAQMyLjmYAQCgAQHIAQjAAQE&sclient=gws-wiz-serp).) \n", + "\n", + "For example, this notebook is called `mwrret2turn.c1`, where `mwrret2turn` is the \"datastream name\", and the `{process.ds_class_level}}` is the \"data level\".\n", + "\n", + "This datastream also contains site `oli` and facility `M1`. (Note: individual datastream might have multiple site-facility pairs.)\n", + "In such a case, the data of this data-stream is stored at `/data/archive/oli/olimwrret2turnM1.c1`, which is in the format of `//.`. We can use the following method to assign the data-stream directory `datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )`\n", + "\n", + "The data files under datastream_dir also follows naming conventions. But once reach the datastream_dir level, the most import file naming convention to differentiate the files is \"yyyyMMdd.hhmmss\", which comes handy to filter out files based on datetime. For example, we can use `glob.glob(f'{datastream_dir}/*.200709*.*')` to filter files in 2007 September.\n", + "\n", + "(Note: refer to the https://adc.arm.gov/discovery/#/ and https://adc.arm.gov/solr8/metadata/select API to explore ARM datastream and assoicated available sites and facilities\n", + "\n", + "Please see the following examples in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d7e9eb85", + "metadata": {}, + "outputs": [], + "source": [ + "# Verify if DATA_DIR path exists\n", + "DATA_DIR = \"/data/archive\"\n", + "os.path.exists(DATA_DIR)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "586993fd", + "metadata": {}, + "outputs": [], + "source": [ + "# Speicify datastream_dir following the path conventions and check its existence\n", + "DATASTREAM_NAME = \"mwrret2turn\"\n", + "DATA_LEVEL = \"c1\"\n", + "site = \"oli\"\n", + "facility = \"M1\"\n", + "datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )\n", + "print(datastream_dir)\n", + "print(os.path.exists(datastream_dir))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0742f7c1", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: list 5 (random) files under datastream_dir\n", + "files = os.listdir(datastream_dir)\n", + "files[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39b98a36", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: get most recent file\n", + "list_of_files = glob.glob(f\"{datastream_dir}/*\") # * means all if need specific format then *.csv\n", + "latest_file = max(list_of_files, key=os.path.getctime)\n", + "latest_file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "902d514e", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus sort datastream files based on datetime\n", + "files = os.listdir(datastream_dir)\n", + "file_sorted = files.copy()\n", + "file_sorted.sort() \n", + "print(file_sorted[:5])\n", + "\n", + "# to reverse\n", + "file_sorted_reverse = files.copy() \n", + "file_sorted_reverse.sort(reverse=True)\n", + "print(file_sorted_reverse[:5])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec5923b2", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: pattern matching\n", + "# filter the 200709** files under datastream_dir\n", + "files_filter = glob.glob(f'{datastream_dir}/*.200709*.*')\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c4a4aa18", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: use armnotebook_utils.file_filter (TODO) to filter files based on datastream info \n" + ] + }, + { + "cell_type": "markdown", + "id": "cfeb9efc", + "metadata": {}, + "source": [ + "### Load data\n", + "The arm data is stored in [NetCDF](https://en.wikipedia.org/wiki/NetCDF#:~:text=%22NetCDF%20(network%20Common%20Data%20Form,format%20for%20representing%20scientific%20data.) format. We can use xarray's `open_dataset` method to load single file, or `open_mfdataset` to open multiple files. (Note: the latter will still return a single xarray dataset object by combining multiple files.)\n", + "\n", + "Note: open_dataset keeps the file handle open and lazy loads its contents. All parameters are passed directly to open_dataset. It is a preferable method over load_dataset for memory-effiency. (more details, please see [xarray.open_dataset](https://docs.xarray.dev/en/stable/generated/xarray.open_dataset.html))\n", + "\n", + "See the following example in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a440a329", + "metadata": {}, + "outputs": [], + "source": [ + "# open single file\n", + "full_path = latest_file\n", + "print(full_path)\n", + "ds_single = xr.open_dataset(full_path)\n", + "ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0143a3d", + "metadata": {}, + "outputs": [], + "source": [ + "print(ds_single)\n", + "print(type(ds_single))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c0f8939", + "metadata": {}, + "outputs": [], + "source": [ + "# open multiple files\n", + "n_files = 3\n", + "full_paths = [os.path.join(datastream_dir, f_path) for f_path in file_sorted_reverse[:n_files]]\n", + "print(full_paths)\n", + "try: # Note: sometimes multiple files cannot be merged, so we used try except here.\n", + " ds_mutiple = xr.open_mfdataset(full_paths)\n", + " # ds_mutiple\n", + " print(type(ds_mutiple))\n", + " print(ds_mutiple)\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "3fa59943", + "metadata": {}, + "source": [ + "## Explore the data" + ] + }, + { + "cell_type": "markdown", + "id": "571f69b9", + "metadata": {}, + "source": [ + "### NetCDF Data structure \n", + "\n", + "\n", + "(If you are confident with NetCDF basics and xarray essentials, feel free to skip this session.)\n", + "Before we dive into data exploration, there are some eseential concpets we should be familiar with (shown below): \n", + "* Dataset\n", + "* Data array\n", + "* Variable\n", + "* Dimenssion\n", + "* Coordinate\n", + "* Data Type\n", + "* Meta Data (Attributes)\n", + "\n", + "We will not go into details about NetCDF basics and here are some references you might find helpful\n", + "* [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#)\n", + "* [Network Common Data Form (NetCDF)](https://www.unidata.ucar.edu/software/netcdf/) and [A Brief History of (netCDF) Time](https://www.unidata.ucar.edu/software/netcdf/time/recs.html)\n", + "\n", + "\n", + "\n", + "### Xarray essentials\n", + "Earlier we introduced the xarray pacakge and used `open_dataset` and `open_mfdataset` to retrieve NetCDF data file as an xarray Dataset object. Recall that you can review the data in a notebook by using `print(ds)` or `ds`. \n", + "\n", + "Using xarray to retrieve the aforementioned NetCDF basics is straightforwared. In general,\n", + "* Dataset: `ds`\n", + "* Data array: `ds.variables`\n", + "* Variable: `ds.ds.variables`\n", + "* Dimenssion: `ds.dims`\n", + "* Coordinate: `ds.coords`\n", + "* Data Type: `type`\n", + "* Meta Data (Attributes)\n", + "\n", + "Also, here are some references if you are new to xarray\n", + "* [Xarray in 45 minutes](https://tutorial.xarray.dev/overview/xarray-in-45-min.html)\n", + "* [Handling NetCDF Files using XArray for Absolute Beginners](https://towardsdatascience.com/handling-netcdf-files-using-xarray-for-absolute-beginners-111a8ab4463f)\n", + "\n", + "Try the following commands in action\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b3eac323", + "metadata": {}, + "outputs": [], + "source": [ + "ds = ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77ecf85d", + "metadata": {}, + "outputs": [], + "source": [ + "# Dataset \n", + "ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25e7de09", + "metadata": {}, + "outputs": [], + "source": [ + "# Data array, Variable\n", + "# Note: the info can be overwhelming. Do not attempt to grasp everything at the first glance.\n", + "ds.variables" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c41b67e", + "metadata": {}, + "outputs": [], + "source": [ + "# dimenssions\n", + "ds.dims" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "156f1dfc", + "metadata": {}, + "outputs": [], + "source": [ + "# coordinates\n", + "ds.coords" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "277d6064", + "metadata": {}, + "outputs": [], + "source": [ + "# Meta Data (Attributes)\n", + "ds.attrs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d334681f", + "metadata": {}, + "outputs": [], + "source": [ + "# type\n", + "print(type(ds))\n", + "print(type(ds.variables))\n", + "print(type(ds.dims))\n", + "print(type(ds.coords))\n", + "print(type(ds.attrs))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "643399d6", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: retrieve variable names only\n", + "list(ds.variables)" + ] + }, + { + "cell_type": "markdown", + "id": "1cf36878", + "metadata": {}, + "source": [ + "#### Discussion: variable vs. coordinates vs. dimenssions. \n", + "From [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#):\n", + "\n", + "* Dimession: A dimension may be used to represent a real physical dimension, for example, time, latitude, longitude, or height. A dimension might also be used to index other quantities, for example station or model-run-number.\n", + "A netCDF dimension has both a name and a length. A dimension length is an arbitrary positive integer, except that one dimension in a netCDF dataset can have the length UNLIMITED.\n", + "\n", + "* Variables: Variables are used to store the bulk of the data in a netCDF dataset. A variable represents an array of values of the same type. A scalar value is treated as a 0-dimensional array. A variable has a name, a data type, and a shape described by its list of dimensions specified when the variable is created. A variable may also have associated attributes, which may be added, deleted or changed after the variable is created.\n", + "\n", + "* Coordinate (Variables): It is legal for a variable to have the same name as a dimension. Such variables have no special meaning to the netCDF library. However there is a convention that such variables should be treated in a special way by software using this library.\n", + "A variable with the same name as a dimension is called a coordinate variable. It typically defines a physical coordinate corresponding to that dimension.\n", + "\n", + "\n", + "Tips: By definition, coordinates and dimenssions are also variable. For example ds.time is a coordinate but it is also a special variable. But in practice, when we talk about variable, it implies regular/non-coordinate variable. We will use this convention for the remaining notebook. " + ] + }, + { + "cell_type": "markdown", + "id": "d47120f9", + "metadata": {}, + "source": [ + "### Xarray Variable\n", + "\n", + "\n", + "We can use `ds.variables` to access varialbes of a dataset. But many times viewing print-out the whole datasets can be overwhemling. Instead, we would work on the individual variable (also called Xarray data array.) using the `ds[var_name]` syntax, where `var_name` is the name of the variable. See the following examples in action.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5a57e136", + "metadata": {}, + "outputs": [], + "source": [ + "print(type(ds[\"time\"]))\n", + "ds[\"time\"]" + ] + }, + { + "cell_type": "markdown", + "id": "51ec643e", + "metadata": {}, + "source": [ + "#### Variable properties\n", + "For individual varible, the following properties are mostly used with the assocaited xarray method to retrieve them. (assuming var = ds[var_name])\n", + "* name: `var.name`\n", + "* data content: `var.data`\n", + "* attributes: `var.attrs`\n", + "* dimenstions: `var.dims`\n", + "* data type: `var.data.dtype`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a632a525", + "metadata": {}, + "outputs": [], + "source": [ + "var_name = \"time\"\n", + "var = ds[var_name]\n", + "\n", + "print(\"var.name: \\n\", var.name, \"\\n\")\n", + "print(\"var.data: \\n\", var.data, \"\\n\")\n", + "print(\"var.attrs: \\n\", var.attrs, \"\\n\")\n", + "print(\"var.dims: \\n\", var.dims, \"\\n\")\n", + "print(\"var.data.dtype: \\n\", var.data.dtype, \"\\n\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b256b07f", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: indivdual variable info table (collect and display variable info as a tabular format)\n", + "df_info = pd.DataFrame()\n", + "df_info[\"var_name\"] = list(ds.variables)\n", + "df_info[\"dims\"] = df_info.var_name.apply(lambda x: ds[x].dims)\n", + "df_info[\"is_dim\"] = df_info.var_name.apply(lambda x: x in ds.dims)\n", + "df_info[\"n_dim\"] = df_info.var_name.apply(lambda x: len(ds[x].dims))\n", + "df_info[\"attrs\"] = df_info.var_name.apply(lambda x: ds[x].attrs)\n", + "df_info[\"dtype\"] = df_info.var_name.apply(lambda x: ds[x].data.dtype)\n", + "df_info" + ] + }, + { + "cell_type": "markdown", + "id": "e6a36b96", + "metadata": {}, + "source": [ + "### Data cleaning/Preprocessing (skipped)\n", + "Data cleaning and preprocessing is an important stage in the the data analysis pipeline. However it will be out of the scope of this notebook. For more information about data cleaning and preprocessing basics, here are some references. \n", + "* [Xarray Fundamentals](https://earth-env-data-science.github.io/lectures/xarray/xarray.html)\n", + "* [Xarray Tutorial — Pangeo Gallery documentation](http://gallery.pangeo.io/repos/pangeo-data/pangeo-tutorial-gallery/xarray.html)\n", + "* [Pythonic Data Cleaning With pandas and NumPy](https://realpython.com/python-data-cleaning-numpy-pandas/)\n", + "* [Pandas - Cleaning Data](https://www.w3schools.com/python/pandas/pandas_cleaning.asp)" + ] + }, + { + "cell_type": "markdown", + "id": "ae6e1a69", + "metadata": {}, + "source": [ + "### Plotting\n", + "Data visualization (or plotting) is another important data analysis topic and deserves its own discussion. Here in this notebook we will only demonstrate basic tool for simple data visualization tasks.\n", + "\n", + "Here are some reference you might find useful:\n", + "* xarray plotting: https://docs.xarray.dev/en/stable/user-guide/plotting.html\n", + "* Atmospheric Community Toolkit (ACT): https://arm-doe.github.io/ACT/index.html\n", + "\n", + "Note: this notebook is auto-generated using a template. It uses a general idea to select variable(s) to plot and is not customized to each indivdual notebook. Feel free to change the variables in intrrests, especially certain figure is failed to plot." + ] + }, + { + "cell_type": "markdown", + "id": "7e417fb2", + "metadata": {}, + "source": [ + "#### 1-dimenssional basic time series plot\n", + "\n", + "For the following plot we would like to find variables such that\n", + "* it has one and only one dimession\n", + "* \"time\" is its coordinate variable\n", + "* it is not a dimenssion itself,\n", + "* it is not a special variable with substrings within [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "\n", + "Please see the following example in action. (For more details about pandas filtering, please see the following references.)\n", + "* [pandas: multiple conditions while indexing data frame](https://stackoverflow.com/questions/22591174/pandas-multiple-conditions-while-indexing-data-frame-unexpected-behavior)\n", + "* [pandas.DataFrame.apply](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.apply.html)\n", + "* [How to test if a string contains one of the substrings in a list...](https://stackoverflow.com/questions/26577516/how-to-test-if-a-string-contains-one-of-the-substrings-in-a-list-in-pandas" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30edac2d", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==1, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter = df_info[(df_info.n_dim == 1) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4777a659", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_1d = df_filter.var_name.values[0]\n", + " var_1d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb5ae985", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " ds[var_1d].plot()\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "9e41066e", + "metadata": {}, + "source": [ + "#### 2-dimenssional basic plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09bd4adc", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==2, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_2 = df_info[(df_info.n_dim == 2) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2cdea1a9", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_2d = df_filter_2.var_name.values[0]\n", + " var_2d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5f48879", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " print(ds[var_2d].dims)\n", + " # ds[var_2d].plot()\n", + "\n", + " # conventionally, use \"time\" as x-axis\n", + " ds[var_2d].plot(x=\"time\")\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "7554b2a6", + "metadata": {}, + "source": [ + "#### qc-plotting (optional)\n", + "\n", + "Note: act qc plotting has more strict requirement, one of them is the associated qc variable need to have \"flag_masks\" attributes. Which is added by using ds = act.io.armfiles.read_netcdf(files_list), then ds.clean.cleanup()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71c2096f", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " ds_act = act.io.armfiles.read_netcdf(full_path)\n", + " print(type(ds_act))\n", + " ds_act.clean.cleanup()\n", + "\n", + " # or \n", + " # ds.clean.cleanup()\n", + "except Exception as e:\n", + " print(\"ERROR\", e)\n", + " ds_act = ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "844f8505", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter valid variables for ACT qc plotting\n", + "condition = (\"qc_\" + df_info.var_name).apply(lambda x: ds_act[x].attrs.get(\"flag_masks\") is not None \n", + " if x in list(ds.variables) else False)\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_3 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings))) &\n", + " condition\n", + " ]\n", + "df_filter_3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd38cc6", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "try:\n", + " qc_variable = df_filter_3.var_name.values[0]\n", + " print(qc_variable)\n", + "except Exception as e:\n", + " print(e)\n", + " \n", + "try:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + " qc_ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "d76e4d27", + "metadata": {}, + "source": [ + "#### bonus: choose variables to plot from a dropdown menu " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb733804", + "metadata": {}, + "outputs": [], + "source": [ + "# Valid variables filtering\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_4 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82d5c20d", + "metadata": {}, + "outputs": [], + "source": [ + "# example 1: using xarray plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2fc9aaa4", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# fig, ax = plt.subplots(figsize=(10, 4))\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "# fig.clear() # Remove old lines from plot and plot new one\n", + "# if len(ds[var].dims)==2:\n", + "# ds[var].plot(x=\"time\", add_colorbar=False)\n", + "# else:\n", + "# ds[var].plot()\n", + "# plt.grid()\n", + "# plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fabdd802", + "metadata": {}, + "outputs": [], + "source": [ + "# example 2: using act plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "962b4186", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "\n", + "\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "\n", + "# i_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + "# i_display.add_subplots((1,), figsize=(10, 4))\n", + "# ax = i_display.plot(var, subplot_index=(0,), set_title=f\"{var} ({ds_act[var].attrs['long_name']})\",)\n", + "\n", + "# ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "# ax.grid()\n", + "# plt.show()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/MWRRETV2/mwrret2turn.c1.ipynb b/VAPs/quicklook/MWRRETV2/mwrret2turn.c1.ipynb new file mode 100644 index 00000000..a7c4f415 --- /dev/null +++ b/VAPs/quicklook/MWRRETV2/mwrret2turn.c1.ipynb @@ -0,0 +1,339 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# MWRRET2TURN.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/mwrretv2) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'mwrret2turn'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2019-09-30', 'facility': 'M1', 'site': 'oli', 'start_date': '2014-11-11'}, {'end_date': '2022-07-31', 'facility': 'C1', 'site': 'ena', 'start_date': '2014-05-01'}, {'end_date': '2022-10-01', 'facility': 'M1', 'site': 'hou', 'start_date': '2021-10-01'}, {'end_date': '2019-10-31', 'facility': 'C1', 'site': 'sgp', 'start_date': '2011-10-01'}, {'end_date': '2022-08-31', 'facility': 'E32', 'site': 'sgp', 'start_date': '2016-06-29'}, {'end_date': '2022-07-08', 'facility': 'E37', 'site': 'sgp', 'start_date': '2016-05-01'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2019-10-29'\n", + "date_end = '2019-10-31'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['phys_pwv', 'phys_pwv_uncertainty', 'phys_lwp']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'phys_pwv'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'phys_pwv'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/NAVMET-AIR/NAVMET-AIR_tutorial.ipynb b/VAPs/quicklook/NAVMET-AIR/NAVMET-AIR_tutorial.ipynb new file mode 100644 index 00000000..9cc0606e --- /dev/null +++ b/VAPs/quicklook/NAVMET-AIR/NAVMET-AIR_tutorial.ipynb @@ -0,0 +1,838 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# AAFNAVIWG.C1 Notebook\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/navmet-air) for more information about this vap." + ] + }, + { + "cell_type": "markdown", + "id": "97097763", + "metadata": {}, + "source": [ + "In this notebook, we demonstrate the workflow to explore ARM vap data (using aafnaviwg as an example.) Value-added products (VAPs) are higher-order data products that have been analyzed and processed to ease scientists’ use of ARM data in atmospheric research and global climate models. \n", + "Here is the main content we will cover." + ] + }, + { + "cell_type": "markdown", + "id": "eddec40f", + "metadata": {}, + "source": [ + "# Table of Content\n", + "## Access the data\n", + "* How to retrieve the data\n", + "* Data path and file name conventions\n", + "* Load data\n", + "## Explore the data\n", + "* NetCDF Data structure\n", + "* Xarray essentials\n", + "* Xarray Variable\n", + "## Plot the data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "import random\n", + "\n", + "import glob\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "2372d5be", + "metadata": {}, + "source": [ + "## Access the data" + ] + }, + { + "cell_type": "markdown", + "id": "5b0d9684", + "metadata": {}, + "source": [ + "### How to retrieve the data\n", + "We assume the path \"/data/archive\" is available where you are running this notebook. You can use `os.path.exists(\"/data/archive\")` to verify if the path exists at your machine.\n", + "\n", + "\n", + "### Data path and file name conventions\n", + "There several common terminologies regarding ARM data, for example, data-stream-name, data-level, site, facility, instrument, etc. (For more details, please see [ARM Data File Standards Version 1.2](https://www.google.com/search?q=arm+datastream+facility+cite+definition&rlz=1C1GCEJ_enUS1029US1029&ei=hb41ZICrBPukqtsPvMODIA&ved=0ahUKEwjAgeiS0aL-AhV7kmoFHbzhAAQQ4dUDCBA&uact=5&oq=arm+datastream+facility+cite+definition&gs_lcp=Cgxnd3Mtd2l6LXNlcnAQAzIFCAAQogQyBQgAEKIEMgUIABCiBDIFCAAQogQyBQgAEKIEOgoIABBHENYEELADOgoIIRCgARDDBBAKSgQIQRgAUJ4DWIoOYO0PaAFwAXgAgAGMAYgBuQmSAQMyLjmYAQCgAQHIAQjAAQE&sclient=gws-wiz-serp).) \n", + "\n", + "For example, this notebook is called `aafnaviwg.c1`, where `aafnaviwg` is the \"datastream name\", and the `{process.ds_class_level}}` is the \"data level\".\n", + "\n", + "This datastream also contains site `cor` and facility `F1`. (Note: individual datastream might have multiple site-facility pairs.)\n", + "In such a case, the data of this data-stream is stored at `/data/archive/cor/coraafnaviwgF1.c1`, which is in the format of `//.`. We can use the following method to assign the data-stream directory `datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )`\n", + "\n", + "The data files under datastream_dir also follows naming conventions. But once reach the datastream_dir level, the most import file naming convention to differentiate the files is \"yyyyMMdd.hhmmss\", which comes handy to filter out files based on datetime. For example, we can use `glob.glob(f'{datastream_dir}/*.200709*.*')` to filter files in 2007 September.\n", + "\n", + "(Note: refer to the https://adc.arm.gov/discovery/#/ and https://adc.arm.gov/solr8/metadata/select API to explore ARM datastream and assoicated available sites and facilities\n", + "\n", + "Please see the following examples in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d7e9eb85", + "metadata": {}, + "outputs": [], + "source": [ + "# Verify if DATA_DIR path exists\n", + "DATA_DIR = \"/data/archive\"\n", + "os.path.exists(DATA_DIR)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "586993fd", + "metadata": {}, + "outputs": [], + "source": [ + "# Speicify datastream_dir following the path conventions and check its existence\n", + "DATASTREAM_NAME = \"aafnaviwg\"\n", + "DATA_LEVEL = \"c1\"\n", + "site = \"cor\"\n", + "facility = \"F1\"\n", + "datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )\n", + "print(datastream_dir)\n", + "print(os.path.exists(datastream_dir))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0742f7c1", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: list 5 (random) files under datastream_dir\n", + "files = os.listdir(datastream_dir)\n", + "files[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39b98a36", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: get most recent file\n", + "list_of_files = glob.glob(f\"{datastream_dir}/*\") # * means all if need specific format then *.csv\n", + "latest_file = max(list_of_files, key=os.path.getctime)\n", + "latest_file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "902d514e", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus sort datastream files based on datetime\n", + "files = os.listdir(datastream_dir)\n", + "file_sorted = files.copy()\n", + "file_sorted.sort() \n", + "print(file_sorted[:5])\n", + "\n", + "# to reverse\n", + "file_sorted_reverse = files.copy() \n", + "file_sorted_reverse.sort(reverse=True)\n", + "print(file_sorted_reverse[:5])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec5923b2", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: pattern matching\n", + "# filter the 200709** files under datastream_dir\n", + "files_filter = glob.glob(f'{datastream_dir}/*.200709*.*')\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c4a4aa18", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: use armnotebook_utils.file_filter (TODO) to filter files based on datastream info \n" + ] + }, + { + "cell_type": "markdown", + "id": "cfeb9efc", + "metadata": {}, + "source": [ + "### Load data\n", + "The arm data is stored in [NetCDF](https://en.wikipedia.org/wiki/NetCDF#:~:text=%22NetCDF%20(network%20Common%20Data%20Form,format%20for%20representing%20scientific%20data.) format. We can use xarray's `open_dataset` method to load single file, or `open_mfdataset` to open multiple files. (Note: the latter will still return a single xarray dataset object by combining multiple files.)\n", + "\n", + "Note: open_dataset keeps the file handle open and lazy loads its contents. All parameters are passed directly to open_dataset. It is a preferable method over load_dataset for memory-effiency. (more details, please see [xarray.open_dataset](https://docs.xarray.dev/en/stable/generated/xarray.open_dataset.html))\n", + "\n", + "See the following example in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a440a329", + "metadata": {}, + "outputs": [], + "source": [ + "# open single file\n", + "full_path = latest_file\n", + "print(full_path)\n", + "ds_single = xr.open_dataset(full_path)\n", + "ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0143a3d", + "metadata": {}, + "outputs": [], + "source": [ + "print(ds_single)\n", + "print(type(ds_single))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c0f8939", + "metadata": {}, + "outputs": [], + "source": [ + "# open multiple files\n", + "n_files = 3\n", + "full_paths = [os.path.join(datastream_dir, f_path) for f_path in file_sorted_reverse[:n_files]]\n", + "print(full_paths)\n", + "try: # Note: sometimes multiple files cannot be merged, so we used try except here.\n", + " ds_mutiple = xr.open_mfdataset(full_paths)\n", + " # ds_mutiple\n", + " print(type(ds_mutiple))\n", + " print(ds_mutiple)\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "3fa59943", + "metadata": {}, + "source": [ + "## Explore the data" + ] + }, + { + "cell_type": "markdown", + "id": "571f69b9", + "metadata": {}, + "source": [ + "### NetCDF Data structure \n", + "\n", + "\n", + "(If you are confident with NetCDF basics and xarray essentials, feel free to skip this session.)\n", + "Before we dive into data exploration, there are some eseential concpets we should be familiar with (shown below): \n", + "* Dataset\n", + "* Data array\n", + "* Variable\n", + "* Dimenssion\n", + "* Coordinate\n", + "* Data Type\n", + "* Meta Data (Attributes)\n", + "\n", + "We will not go into details about NetCDF basics and here are some references you might find helpful\n", + "* [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#)\n", + "* [Network Common Data Form (NetCDF)](https://www.unidata.ucar.edu/software/netcdf/) and [A Brief History of (netCDF) Time](https://www.unidata.ucar.edu/software/netcdf/time/recs.html)\n", + "\n", + "\n", + "\n", + "### Xarray essentials\n", + "Earlier we introduced the xarray pacakge and used `open_dataset` and `open_mfdataset` to retrieve NetCDF data file as an xarray Dataset object. Recall that you can review the data in a notebook by using `print(ds)` or `ds`. \n", + "\n", + "Using xarray to retrieve the aforementioned NetCDF basics is straightforwared. In general,\n", + "* Dataset: `ds`\n", + "* Data array: `ds.variables`\n", + "* Variable: `ds.ds.variables`\n", + "* Dimenssion: `ds.dims`\n", + "* Coordinate: `ds.coords`\n", + "* Data Type: `type`\n", + "* Meta Data (Attributes)\n", + "\n", + "Also, here are some references if you are new to xarray\n", + "* [Xarray in 45 minutes](https://tutorial.xarray.dev/overview/xarray-in-45-min.html)\n", + "* [Handling NetCDF Files using XArray for Absolute Beginners](https://towardsdatascience.com/handling-netcdf-files-using-xarray-for-absolute-beginners-111a8ab4463f)\n", + "\n", + "Try the following commands in action\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b3eac323", + "metadata": {}, + "outputs": [], + "source": [ + "ds = ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77ecf85d", + "metadata": {}, + "outputs": [], + "source": [ + "# Dataset \n", + "ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25e7de09", + "metadata": {}, + "outputs": [], + "source": [ + "# Data array, Variable\n", + "# Note: the info can be overwhelming. Do not attempt to grasp everything at the first glance.\n", + "ds.variables" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c41b67e", + "metadata": {}, + "outputs": [], + "source": [ + "# dimenssions\n", + "ds.dims" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "156f1dfc", + "metadata": {}, + "outputs": [], + "source": [ + "# coordinates\n", + "ds.coords" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "277d6064", + "metadata": {}, + "outputs": [], + "source": [ + "# Meta Data (Attributes)\n", + "ds.attrs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d334681f", + "metadata": {}, + "outputs": [], + "source": [ + "# type\n", + "print(type(ds))\n", + "print(type(ds.variables))\n", + "print(type(ds.dims))\n", + "print(type(ds.coords))\n", + "print(type(ds.attrs))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "643399d6", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: retrieve variable names only\n", + "list(ds.variables)" + ] + }, + { + "cell_type": "markdown", + "id": "1cf36878", + "metadata": {}, + "source": [ + "#### Discussion: variable vs. coordinates vs. dimenssions. \n", + "From [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#):\n", + "\n", + "* Dimession: A dimension may be used to represent a real physical dimension, for example, time, latitude, longitude, or height. A dimension might also be used to index other quantities, for example station or model-run-number.\n", + "A netCDF dimension has both a name and a length. A dimension length is an arbitrary positive integer, except that one dimension in a netCDF dataset can have the length UNLIMITED.\n", + "\n", + "* Variables: Variables are used to store the bulk of the data in a netCDF dataset. A variable represents an array of values of the same type. A scalar value is treated as a 0-dimensional array. A variable has a name, a data type, and a shape described by its list of dimensions specified when the variable is created. A variable may also have associated attributes, which may be added, deleted or changed after the variable is created.\n", + "\n", + "* Coordinate (Variables): It is legal for a variable to have the same name as a dimension. Such variables have no special meaning to the netCDF library. However there is a convention that such variables should be treated in a special way by software using this library.\n", + "A variable with the same name as a dimension is called a coordinate variable. It typically defines a physical coordinate corresponding to that dimension.\n", + "\n", + "\n", + "Tips: By definition, coordinates and dimenssions are also variable. For example ds.time is a coordinate but it is also a special variable. But in practice, when we talk about variable, it implies regular/non-coordinate variable. We will use this convention for the remaining notebook. " + ] + }, + { + "cell_type": "markdown", + "id": "d47120f9", + "metadata": {}, + "source": [ + "### Xarray Variable\n", + "\n", + "\n", + "We can use `ds.variables` to access varialbes of a dataset. But many times viewing print-out the whole datasets can be overwhemling. Instead, we would work on the individual variable (also called Xarray data array.) using the `ds[var_name]` syntax, where `var_name` is the name of the variable. See the following examples in action.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5a57e136", + "metadata": {}, + "outputs": [], + "source": [ + "print(type(ds[\"time\"]))\n", + "ds[\"time\"]" + ] + }, + { + "cell_type": "markdown", + "id": "51ec643e", + "metadata": {}, + "source": [ + "#### Variable properties\n", + "For individual varible, the following properties are mostly used with the assocaited xarray method to retrieve them. (assuming var = ds[var_name])\n", + "* name: `var.name`\n", + "* data content: `var.data`\n", + "* attributes: `var.attrs`\n", + "* dimenstions: `var.dims`\n", + "* data type: `var.data.dtype`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a632a525", + "metadata": {}, + "outputs": [], + "source": [ + "var_name = \"time\"\n", + "var = ds[var_name]\n", + "\n", + "print(\"var.name: \\n\", var.name, \"\\n\")\n", + "print(\"var.data: \\n\", var.data, \"\\n\")\n", + "print(\"var.attrs: \\n\", var.attrs, \"\\n\")\n", + "print(\"var.dims: \\n\", var.dims, \"\\n\")\n", + "print(\"var.data.dtype: \\n\", var.data.dtype, \"\\n\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b256b07f", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: indivdual variable info table (collect and display variable info as a tabular format)\n", + "df_info = pd.DataFrame()\n", + "df_info[\"var_name\"] = list(ds.variables)\n", + "df_info[\"dims\"] = df_info.var_name.apply(lambda x: ds[x].dims)\n", + "df_info[\"is_dim\"] = df_info.var_name.apply(lambda x: x in ds.dims)\n", + "df_info[\"n_dim\"] = df_info.var_name.apply(lambda x: len(ds[x].dims))\n", + "df_info[\"attrs\"] = df_info.var_name.apply(lambda x: ds[x].attrs)\n", + "df_info[\"dtype\"] = df_info.var_name.apply(lambda x: ds[x].data.dtype)\n", + "df_info" + ] + }, + { + "cell_type": "markdown", + "id": "e6a36b96", + "metadata": {}, + "source": [ + "### Data cleaning/Preprocessing (skipped)\n", + "Data cleaning and preprocessing is an important stage in the the data analysis pipeline. However it will be out of the scope of this notebook. For more information about data cleaning and preprocessing basics, here are some references. \n", + "* [Xarray Fundamentals](https://earth-env-data-science.github.io/lectures/xarray/xarray.html)\n", + "* [Xarray Tutorial — Pangeo Gallery documentation](http://gallery.pangeo.io/repos/pangeo-data/pangeo-tutorial-gallery/xarray.html)\n", + "* [Pythonic Data Cleaning With pandas and NumPy](https://realpython.com/python-data-cleaning-numpy-pandas/)\n", + "* [Pandas - Cleaning Data](https://www.w3schools.com/python/pandas/pandas_cleaning.asp)" + ] + }, + { + "cell_type": "markdown", + "id": "ae6e1a69", + "metadata": {}, + "source": [ + "### Plotting\n", + "Data visualization (or plotting) is another important data analysis topic and deserves its own discussion. Here in this notebook we will only demonstrate basic tool for simple data visualization tasks.\n", + "\n", + "Here are some reference you might find useful:\n", + "* xarray plotting: https://docs.xarray.dev/en/stable/user-guide/plotting.html\n", + "* Atmospheric Community Toolkit (ACT): https://arm-doe.github.io/ACT/index.html\n", + "\n", + "Note: this notebook is auto-generated using a template. It uses a general idea to select variable(s) to plot and is not customized to each indivdual notebook. Feel free to change the variables in intrrests, especially certain figure is failed to plot." + ] + }, + { + "cell_type": "markdown", + "id": "7e417fb2", + "metadata": {}, + "source": [ + "#### 1-dimenssional basic time series plot\n", + "\n", + "For the following plot we would like to find variables such that\n", + "* it has one and only one dimession\n", + "* \"time\" is its coordinate variable\n", + "* it is not a dimenssion itself,\n", + "* it is not a special variable with substrings within [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "\n", + "Please see the following example in action. (For more details about pandas filtering, please see the following references.)\n", + "* [pandas: multiple conditions while indexing data frame](https://stackoverflow.com/questions/22591174/pandas-multiple-conditions-while-indexing-data-frame-unexpected-behavior)\n", + "* [pandas.DataFrame.apply](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.apply.html)\n", + "* [How to test if a string contains one of the substrings in a list...](https://stackoverflow.com/questions/26577516/how-to-test-if-a-string-contains-one-of-the-substrings-in-a-list-in-pandas" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30edac2d", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==1, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter = df_info[(df_info.n_dim == 1) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4777a659", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_1d = df_filter.var_name.values[0]\n", + " var_1d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb5ae985", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " ds[var_1d].plot()\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "9e41066e", + "metadata": {}, + "source": [ + "#### 2-dimenssional basic plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09bd4adc", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==2, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_2 = df_info[(df_info.n_dim == 2) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2cdea1a9", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_2d = df_filter_2.var_name.values[0]\n", + " var_2d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5f48879", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " print(ds[var_2d].dims)\n", + " # ds[var_2d].plot()\n", + "\n", + " # conventionally, use \"time\" as x-axis\n", + " ds[var_2d].plot(x=\"time\")\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "7554b2a6", + "metadata": {}, + "source": [ + "#### qc-plotting (optional)\n", + "\n", + "Note: act qc plotting has more strict requirement, one of them is the associated qc variable need to have \"flag_masks\" attributes. Which is added by using ds = act.io.armfiles.read_netcdf(files_list), then ds.clean.cleanup()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71c2096f", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " ds_act = act.io.armfiles.read_netcdf(full_path)\n", + " print(type(ds_act))\n", + " ds_act.clean.cleanup()\n", + "\n", + " # or \n", + " # ds.clean.cleanup()\n", + "except Exception as e:\n", + " print(\"ERROR\", e)\n", + " ds_act = ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "844f8505", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter valid variables for ACT qc plotting\n", + "condition = (\"qc_\" + df_info.var_name).apply(lambda x: ds_act[x].attrs.get(\"flag_masks\") is not None \n", + " if x in list(ds.variables) else False)\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_3 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings))) &\n", + " condition\n", + " ]\n", + "df_filter_3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd38cc6", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "try:\n", + " qc_variable = df_filter_3.var_name.values[0]\n", + " print(qc_variable)\n", + "except Exception as e:\n", + " print(e)\n", + " \n", + "try:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + " qc_ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "d76e4d27", + "metadata": {}, + "source": [ + "#### bonus: choose variables to plot from a dropdown menu " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb733804", + "metadata": {}, + "outputs": [], + "source": [ + "# Valid variables filtering\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_4 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82d5c20d", + "metadata": {}, + "outputs": [], + "source": [ + "# example 1: using xarray plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2fc9aaa4", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# fig, ax = plt.subplots(figsize=(10, 4))\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "# fig.clear() # Remove old lines from plot and plot new one\n", + "# if len(ds[var].dims)==2:\n", + "# ds[var].plot(x=\"time\", add_colorbar=False)\n", + "# else:\n", + "# ds[var].plot()\n", + "# plt.grid()\n", + "# plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fabdd802", + "metadata": {}, + "outputs": [], + "source": [ + "# example 2: using act plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "962b4186", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "\n", + "\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "\n", + "# i_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + "# i_display.add_subplots((1,), figsize=(10, 4))\n", + "# ax = i_display.plot(var, subplot_index=(0,), set_title=f\"{var} ({ds_act[var].attrs['long_name']})\",)\n", + "\n", + "# ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "# ax.grid()\n", + "# plt.show()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/NAVMET-AIR/aafnaviwg.c1.ipynb b/VAPs/quicklook/NAVMET-AIR/aafnaviwg.c1.ipynb new file mode 100644 index 00000000..b8e4dbdc --- /dev/null +++ b/VAPs/quicklook/NAVMET-AIR/aafnaviwg.c1.ipynb @@ -0,0 +1,297 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# AAFNAVIWG.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/navmet-air) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'aafnaviwg'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2018-12-08', 'facility': 'F1', 'site': 'cor', 'start_date': '2018-11-04'}, {'end_date': '2018-02-19', 'facility': 'F1', 'site': 'ena', 'start_date': '2017-03-31'}, {'end_date': '2016-09-22', 'facility': 'F1', 'site': 'sgp', 'start_date': '2016-04-25'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'F1' )\n", + "\n", + "date_start = '2016-09-20'\n", + "date_end = '2016-09-22'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['wgs_alt', 'press_alt', 'radar_alt']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'wgs_alt'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/NDROP/NDROP_tutorial.ipynb b/VAPs/quicklook/NDROP/NDROP_tutorial.ipynb new file mode 100644 index 00000000..6916edf6 --- /dev/null +++ b/VAPs/quicklook/NDROP/NDROP_tutorial.ipynb @@ -0,0 +1,838 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# NDROPMFRSR.C1 Notebook\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/ndrop) for more information about this vap." + ] + }, + { + "cell_type": "markdown", + "id": "97097763", + "metadata": {}, + "source": [ + "In this notebook, we demonstrate the workflow to explore ARM vap data (using ndropmfrsr as an example.) Value-added products (VAPs) are higher-order data products that have been analyzed and processed to ease scientists’ use of ARM data in atmospheric research and global climate models. \n", + "Here is the main content we will cover." + ] + }, + { + "cell_type": "markdown", + "id": "eddec40f", + "metadata": {}, + "source": [ + "# Table of Content\n", + "## Access the data\n", + "* How to retrieve the data\n", + "* Data path and file name conventions\n", + "* Load data\n", + "## Explore the data\n", + "* NetCDF Data structure\n", + "* Xarray essentials\n", + "* Xarray Variable\n", + "## Plot the data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "import random\n", + "\n", + "import glob\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "2372d5be", + "metadata": {}, + "source": [ + "## Access the data" + ] + }, + { + "cell_type": "markdown", + "id": "5b0d9684", + "metadata": {}, + "source": [ + "### How to retrieve the data\n", + "We assume the path \"/data/archive\" is available where you are running this notebook. You can use `os.path.exists(\"/data/archive\")` to verify if the path exists at your machine.\n", + "\n", + "\n", + "### Data path and file name conventions\n", + "There several common terminologies regarding ARM data, for example, data-stream-name, data-level, site, facility, instrument, etc. (For more details, please see [ARM Data File Standards Version 1.2](https://www.google.com/search?q=arm+datastream+facility+cite+definition&rlz=1C1GCEJ_enUS1029US1029&ei=hb41ZICrBPukqtsPvMODIA&ved=0ahUKEwjAgeiS0aL-AhV7kmoFHbzhAAQQ4dUDCBA&uact=5&oq=arm+datastream+facility+cite+definition&gs_lcp=Cgxnd3Mtd2l6LXNlcnAQAzIFCAAQogQyBQgAEKIEMgUIABCiBDIFCAAQogQyBQgAEKIEOgoIABBHENYEELADOgoIIRCgARDDBBAKSgQIQRgAUJ4DWIoOYO0PaAFwAXgAgAGMAYgBuQmSAQMyLjmYAQCgAQHIAQjAAQE&sclient=gws-wiz-serp).) \n", + "\n", + "For example, this notebook is called `ndropmfrsr.c1`, where `ndropmfrsr` is the \"datastream name\", and the `{process.ds_class_level}}` is the \"data level\".\n", + "\n", + "This datastream also contains site `asi` and facility `M1`. (Note: individual datastream might have multiple site-facility pairs.)\n", + "In such a case, the data of this data-stream is stored at `/data/archive/asi/asindropmfrsrM1.c1`, which is in the format of `//.`. We can use the following method to assign the data-stream directory `datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )`\n", + "\n", + "The data files under datastream_dir also follows naming conventions. But once reach the datastream_dir level, the most import file naming convention to differentiate the files is \"yyyyMMdd.hhmmss\", which comes handy to filter out files based on datetime. For example, we can use `glob.glob(f'{datastream_dir}/*.200709*.*')` to filter files in 2007 September.\n", + "\n", + "(Note: refer to the https://adc.arm.gov/discovery/#/ and https://adc.arm.gov/solr8/metadata/select API to explore ARM datastream and assoicated available sites and facilities\n", + "\n", + "Please see the following examples in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d7e9eb85", + "metadata": {}, + "outputs": [], + "source": [ + "# Verify if DATA_DIR path exists\n", + "DATA_DIR = \"/data/archive\"\n", + "os.path.exists(DATA_DIR)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "586993fd", + "metadata": {}, + "outputs": [], + "source": [ + "# Speicify datastream_dir following the path conventions and check its existence\n", + "DATASTREAM_NAME = \"ndropmfrsr\"\n", + "DATA_LEVEL = \"c1\"\n", + "site = \"asi\"\n", + "facility = \"M1\"\n", + "datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )\n", + "print(datastream_dir)\n", + "print(os.path.exists(datastream_dir))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0742f7c1", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: list 5 (random) files under datastream_dir\n", + "files = os.listdir(datastream_dir)\n", + "files[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39b98a36", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: get most recent file\n", + "list_of_files = glob.glob(f\"{datastream_dir}/*\") # * means all if need specific format then *.csv\n", + "latest_file = max(list_of_files, key=os.path.getctime)\n", + "latest_file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "902d514e", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus sort datastream files based on datetime\n", + "files = os.listdir(datastream_dir)\n", + "file_sorted = files.copy()\n", + "file_sorted.sort() \n", + "print(file_sorted[:5])\n", + "\n", + "# to reverse\n", + "file_sorted_reverse = files.copy() \n", + "file_sorted_reverse.sort(reverse=True)\n", + "print(file_sorted_reverse[:5])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec5923b2", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: pattern matching\n", + "# filter the 200709** files under datastream_dir\n", + "files_filter = glob.glob(f'{datastream_dir}/*.200709*.*')\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c4a4aa18", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: use armnotebook_utils.file_filter (TODO) to filter files based on datastream info \n" + ] + }, + { + "cell_type": "markdown", + "id": "cfeb9efc", + "metadata": {}, + "source": [ + "### Load data\n", + "The arm data is stored in [NetCDF](https://en.wikipedia.org/wiki/NetCDF#:~:text=%22NetCDF%20(network%20Common%20Data%20Form,format%20for%20representing%20scientific%20data.) format. We can use xarray's `open_dataset` method to load single file, or `open_mfdataset` to open multiple files. (Note: the latter will still return a single xarray dataset object by combining multiple files.)\n", + "\n", + "Note: open_dataset keeps the file handle open and lazy loads its contents. All parameters are passed directly to open_dataset. It is a preferable method over load_dataset for memory-effiency. (more details, please see [xarray.open_dataset](https://docs.xarray.dev/en/stable/generated/xarray.open_dataset.html))\n", + "\n", + "See the following example in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a440a329", + "metadata": {}, + "outputs": [], + "source": [ + "# open single file\n", + "full_path = latest_file\n", + "print(full_path)\n", + "ds_single = xr.open_dataset(full_path)\n", + "ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0143a3d", + "metadata": {}, + "outputs": [], + "source": [ + "print(ds_single)\n", + "print(type(ds_single))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c0f8939", + "metadata": {}, + "outputs": [], + "source": [ + "# open multiple files\n", + "n_files = 3\n", + "full_paths = [os.path.join(datastream_dir, f_path) for f_path in file_sorted_reverse[:n_files]]\n", + "print(full_paths)\n", + "try: # Note: sometimes multiple files cannot be merged, so we used try except here.\n", + " ds_mutiple = xr.open_mfdataset(full_paths)\n", + " # ds_mutiple\n", + " print(type(ds_mutiple))\n", + " print(ds_mutiple)\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "3fa59943", + "metadata": {}, + "source": [ + "## Explore the data" + ] + }, + { + "cell_type": "markdown", + "id": "571f69b9", + "metadata": {}, + "source": [ + "### NetCDF Data structure \n", + "\n", + "\n", + "(If you are confident with NetCDF basics and xarray essentials, feel free to skip this session.)\n", + "Before we dive into data exploration, there are some eseential concpets we should be familiar with (shown below): \n", + "* Dataset\n", + "* Data array\n", + "* Variable\n", + "* Dimenssion\n", + "* Coordinate\n", + "* Data Type\n", + "* Meta Data (Attributes)\n", + "\n", + "We will not go into details about NetCDF basics and here are some references you might find helpful\n", + "* [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#)\n", + "* [Network Common Data Form (NetCDF)](https://www.unidata.ucar.edu/software/netcdf/) and [A Brief History of (netCDF) Time](https://www.unidata.ucar.edu/software/netcdf/time/recs.html)\n", + "\n", + "\n", + "\n", + "### Xarray essentials\n", + "Earlier we introduced the xarray pacakge and used `open_dataset` and `open_mfdataset` to retrieve NetCDF data file as an xarray Dataset object. Recall that you can review the data in a notebook by using `print(ds)` or `ds`. \n", + "\n", + "Using xarray to retrieve the aforementioned NetCDF basics is straightforwared. In general,\n", + "* Dataset: `ds`\n", + "* Data array: `ds.variables`\n", + "* Variable: `ds.ds.variables`\n", + "* Dimenssion: `ds.dims`\n", + "* Coordinate: `ds.coords`\n", + "* Data Type: `type`\n", + "* Meta Data (Attributes)\n", + "\n", + "Also, here are some references if you are new to xarray\n", + "* [Xarray in 45 minutes](https://tutorial.xarray.dev/overview/xarray-in-45-min.html)\n", + "* [Handling NetCDF Files using XArray for Absolute Beginners](https://towardsdatascience.com/handling-netcdf-files-using-xarray-for-absolute-beginners-111a8ab4463f)\n", + "\n", + "Try the following commands in action\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b3eac323", + "metadata": {}, + "outputs": [], + "source": [ + "ds = ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77ecf85d", + "metadata": {}, + "outputs": [], + "source": [ + "# Dataset \n", + "ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25e7de09", + "metadata": {}, + "outputs": [], + "source": [ + "# Data array, Variable\n", + "# Note: the info can be overwhelming. Do not attempt to grasp everything at the first glance.\n", + "ds.variables" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c41b67e", + "metadata": {}, + "outputs": [], + "source": [ + "# dimenssions\n", + "ds.dims" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "156f1dfc", + "metadata": {}, + "outputs": [], + "source": [ + "# coordinates\n", + "ds.coords" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "277d6064", + "metadata": {}, + "outputs": [], + "source": [ + "# Meta Data (Attributes)\n", + "ds.attrs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d334681f", + "metadata": {}, + "outputs": [], + "source": [ + "# type\n", + "print(type(ds))\n", + "print(type(ds.variables))\n", + "print(type(ds.dims))\n", + "print(type(ds.coords))\n", + "print(type(ds.attrs))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "643399d6", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: retrieve variable names only\n", + "list(ds.variables)" + ] + }, + { + "cell_type": "markdown", + "id": "1cf36878", + "metadata": {}, + "source": [ + "#### Discussion: variable vs. coordinates vs. dimenssions. \n", + "From [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#):\n", + "\n", + "* Dimession: A dimension may be used to represent a real physical dimension, for example, time, latitude, longitude, or height. A dimension might also be used to index other quantities, for example station or model-run-number.\n", + "A netCDF dimension has both a name and a length. A dimension length is an arbitrary positive integer, except that one dimension in a netCDF dataset can have the length UNLIMITED.\n", + "\n", + "* Variables: Variables are used to store the bulk of the data in a netCDF dataset. A variable represents an array of values of the same type. A scalar value is treated as a 0-dimensional array. A variable has a name, a data type, and a shape described by its list of dimensions specified when the variable is created. A variable may also have associated attributes, which may be added, deleted or changed after the variable is created.\n", + "\n", + "* Coordinate (Variables): It is legal for a variable to have the same name as a dimension. Such variables have no special meaning to the netCDF library. However there is a convention that such variables should be treated in a special way by software using this library.\n", + "A variable with the same name as a dimension is called a coordinate variable. It typically defines a physical coordinate corresponding to that dimension.\n", + "\n", + "\n", + "Tips: By definition, coordinates and dimenssions are also variable. For example ds.time is a coordinate but it is also a special variable. But in practice, when we talk about variable, it implies regular/non-coordinate variable. We will use this convention for the remaining notebook. " + ] + }, + { + "cell_type": "markdown", + "id": "d47120f9", + "metadata": {}, + "source": [ + "### Xarray Variable\n", + "\n", + "\n", + "We can use `ds.variables` to access varialbes of a dataset. But many times viewing print-out the whole datasets can be overwhemling. Instead, we would work on the individual variable (also called Xarray data array.) using the `ds[var_name]` syntax, where `var_name` is the name of the variable. See the following examples in action.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5a57e136", + "metadata": {}, + "outputs": [], + "source": [ + "print(type(ds[\"time\"]))\n", + "ds[\"time\"]" + ] + }, + { + "cell_type": "markdown", + "id": "51ec643e", + "metadata": {}, + "source": [ + "#### Variable properties\n", + "For individual varible, the following properties are mostly used with the assocaited xarray method to retrieve them. (assuming var = ds[var_name])\n", + "* name: `var.name`\n", + "* data content: `var.data`\n", + "* attributes: `var.attrs`\n", + "* dimenstions: `var.dims`\n", + "* data type: `var.data.dtype`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a632a525", + "metadata": {}, + "outputs": [], + "source": [ + "var_name = \"time\"\n", + "var = ds[var_name]\n", + "\n", + "print(\"var.name: \\n\", var.name, \"\\n\")\n", + "print(\"var.data: \\n\", var.data, \"\\n\")\n", + "print(\"var.attrs: \\n\", var.attrs, \"\\n\")\n", + "print(\"var.dims: \\n\", var.dims, \"\\n\")\n", + "print(\"var.data.dtype: \\n\", var.data.dtype, \"\\n\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b256b07f", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: indivdual variable info table (collect and display variable info as a tabular format)\n", + "df_info = pd.DataFrame()\n", + "df_info[\"var_name\"] = list(ds.variables)\n", + "df_info[\"dims\"] = df_info.var_name.apply(lambda x: ds[x].dims)\n", + "df_info[\"is_dim\"] = df_info.var_name.apply(lambda x: x in ds.dims)\n", + "df_info[\"n_dim\"] = df_info.var_name.apply(lambda x: len(ds[x].dims))\n", + "df_info[\"attrs\"] = df_info.var_name.apply(lambda x: ds[x].attrs)\n", + "df_info[\"dtype\"] = df_info.var_name.apply(lambda x: ds[x].data.dtype)\n", + "df_info" + ] + }, + { + "cell_type": "markdown", + "id": "e6a36b96", + "metadata": {}, + "source": [ + "### Data cleaning/Preprocessing (skipped)\n", + "Data cleaning and preprocessing is an important stage in the the data analysis pipeline. However it will be out of the scope of this notebook. For more information about data cleaning and preprocessing basics, here are some references. \n", + "* [Xarray Fundamentals](https://earth-env-data-science.github.io/lectures/xarray/xarray.html)\n", + "* [Xarray Tutorial — Pangeo Gallery documentation](http://gallery.pangeo.io/repos/pangeo-data/pangeo-tutorial-gallery/xarray.html)\n", + "* [Pythonic Data Cleaning With pandas and NumPy](https://realpython.com/python-data-cleaning-numpy-pandas/)\n", + "* [Pandas - Cleaning Data](https://www.w3schools.com/python/pandas/pandas_cleaning.asp)" + ] + }, + { + "cell_type": "markdown", + "id": "ae6e1a69", + "metadata": {}, + "source": [ + "### Plotting\n", + "Data visualization (or plotting) is another important data analysis topic and deserves its own discussion. Here in this notebook we will only demonstrate basic tool for simple data visualization tasks.\n", + "\n", + "Here are some reference you might find useful:\n", + "* xarray plotting: https://docs.xarray.dev/en/stable/user-guide/plotting.html\n", + "* Atmospheric Community Toolkit (ACT): https://arm-doe.github.io/ACT/index.html\n", + "\n", + "Note: this notebook is auto-generated using a template. It uses a general idea to select variable(s) to plot and is not customized to each indivdual notebook. Feel free to change the variables in intrrests, especially certain figure is failed to plot." + ] + }, + { + "cell_type": "markdown", + "id": "7e417fb2", + "metadata": {}, + "source": [ + "#### 1-dimenssional basic time series plot\n", + "\n", + "For the following plot we would like to find variables such that\n", + "* it has one and only one dimession\n", + "* \"time\" is its coordinate variable\n", + "* it is not a dimenssion itself,\n", + "* it is not a special variable with substrings within [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "\n", + "Please see the following example in action. (For more details about pandas filtering, please see the following references.)\n", + "* [pandas: multiple conditions while indexing data frame](https://stackoverflow.com/questions/22591174/pandas-multiple-conditions-while-indexing-data-frame-unexpected-behavior)\n", + "* [pandas.DataFrame.apply](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.apply.html)\n", + "* [How to test if a string contains one of the substrings in a list...](https://stackoverflow.com/questions/26577516/how-to-test-if-a-string-contains-one-of-the-substrings-in-a-list-in-pandas" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30edac2d", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==1, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter = df_info[(df_info.n_dim == 1) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4777a659", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_1d = df_filter.var_name.values[0]\n", + " var_1d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb5ae985", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " ds[var_1d].plot()\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "9e41066e", + "metadata": {}, + "source": [ + "#### 2-dimenssional basic plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09bd4adc", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==2, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_2 = df_info[(df_info.n_dim == 2) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2cdea1a9", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_2d = df_filter_2.var_name.values[0]\n", + " var_2d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5f48879", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " print(ds[var_2d].dims)\n", + " # ds[var_2d].plot()\n", + "\n", + " # conventionally, use \"time\" as x-axis\n", + " ds[var_2d].plot(x=\"time\")\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "7554b2a6", + "metadata": {}, + "source": [ + "#### qc-plotting (optional)\n", + "\n", + "Note: act qc plotting has more strict requirement, one of them is the associated qc variable need to have \"flag_masks\" attributes. Which is added by using ds = act.io.armfiles.read_netcdf(files_list), then ds.clean.cleanup()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71c2096f", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " ds_act = act.io.armfiles.read_netcdf(full_path)\n", + " print(type(ds_act))\n", + " ds_act.clean.cleanup()\n", + "\n", + " # or \n", + " # ds.clean.cleanup()\n", + "except Exception as e:\n", + " print(\"ERROR\", e)\n", + " ds_act = ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "844f8505", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter valid variables for ACT qc plotting\n", + "condition = (\"qc_\" + df_info.var_name).apply(lambda x: ds_act[x].attrs.get(\"flag_masks\") is not None \n", + " if x in list(ds.variables) else False)\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_3 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings))) &\n", + " condition\n", + " ]\n", + "df_filter_3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd38cc6", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "try:\n", + " qc_variable = df_filter_3.var_name.values[0]\n", + " print(qc_variable)\n", + "except Exception as e:\n", + " print(e)\n", + " \n", + "try:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + " qc_ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "d76e4d27", + "metadata": {}, + "source": [ + "#### bonus: choose variables to plot from a dropdown menu " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb733804", + "metadata": {}, + "outputs": [], + "source": [ + "# Valid variables filtering\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_4 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82d5c20d", + "metadata": {}, + "outputs": [], + "source": [ + "# example 1: using xarray plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2fc9aaa4", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# fig, ax = plt.subplots(figsize=(10, 4))\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "# fig.clear() # Remove old lines from plot and plot new one\n", + "# if len(ds[var].dims)==2:\n", + "# ds[var].plot(x=\"time\", add_colorbar=False)\n", + "# else:\n", + "# ds[var].plot()\n", + "# plt.grid()\n", + "# plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fabdd802", + "metadata": {}, + "outputs": [], + "source": [ + "# example 2: using act plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "962b4186", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "\n", + "\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "\n", + "# i_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + "# i_display.add_subplots((1,), figsize=(10, 4))\n", + "# ax = i_display.plot(var, subplot_index=(0,), set_title=f\"{var} ({ds_act[var].attrs['long_name']})\",)\n", + "\n", + "# ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "# ax.grid()\n", + "# plt.show()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/NDROP/ndropmfrsr.c1.ipynb b/VAPs/quicklook/NDROP/ndropmfrsr.c1.ipynb new file mode 100644 index 00000000..82b42fe9 --- /dev/null +++ b/VAPs/quicklook/NDROP/ndropmfrsr.c1.ipynb @@ -0,0 +1,339 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# NDROPMFRSR.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/ndrop) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'ndropmfrsr'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2017-10-31', 'facility': 'M1', 'site': 'asi', 'start_date': '2016-05-23'}, {'end_date': '2019-10-30', 'facility': 'C1', 'site': 'ena', 'start_date': '2014-06-01'}, {'end_date': '2010-12-29', 'facility': 'M1', 'site': 'grw', 'start_date': '2009-05-04'}, {'end_date': '2015-05-18', 'facility': 'E13', 'site': 'sgp', 'start_date': '1999-01-01'}, {'end_date': '2020-09-30', 'facility': 'C1', 'site': 'sgp', 'start_date': '1998-01-01'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'E13' )\n", + "\n", + "date_start = '2015-05-16'\n", + "date_end = '2015-05-18'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['drop_number_conc', 'drop_number_conc_adiabatic', 'lwp_adiabatic']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'optical_depth_instantaneous'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'drop_number_conc'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/NEPHELOMETER/NEPHELOMETER_tutorial.ipynb b/VAPs/quicklook/NEPHELOMETER/NEPHELOMETER_tutorial.ipynb new file mode 100644 index 00000000..d0272bcb --- /dev/null +++ b/VAPs/quicklook/NEPHELOMETER/NEPHELOMETER_tutorial.ipynb @@ -0,0 +1,838 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# AOSNEPHDRY.C1 Notebook\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/nephelometer) for more information about this vap." + ] + }, + { + "cell_type": "markdown", + "id": "97097763", + "metadata": {}, + "source": [ + "In this notebook, we demonstrate the workflow to explore ARM vap data (using aosnephdry as an example.) Value-added products (VAPs) are higher-order data products that have been analyzed and processed to ease scientists’ use of ARM data in atmospheric research and global climate models. \n", + "Here is the main content we will cover." + ] + }, + { + "cell_type": "markdown", + "id": "eddec40f", + "metadata": {}, + "source": [ + "# Table of Content\n", + "## Access the data\n", + "* How to retrieve the data\n", + "* Data path and file name conventions\n", + "* Load data\n", + "## Explore the data\n", + "* NetCDF Data structure\n", + "* Xarray essentials\n", + "* Xarray Variable\n", + "## Plot the data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "import random\n", + "\n", + "import glob\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "2372d5be", + "metadata": {}, + "source": [ + "## Access the data" + ] + }, + { + "cell_type": "markdown", + "id": "5b0d9684", + "metadata": {}, + "source": [ + "### How to retrieve the data\n", + "We assume the path \"/data/archive\" is available where you are running this notebook. You can use `os.path.exists(\"/data/archive\")` to verify if the path exists at your machine.\n", + "\n", + "\n", + "### Data path and file name conventions\n", + "There several common terminologies regarding ARM data, for example, data-stream-name, data-level, site, facility, instrument, etc. (For more details, please see [ARM Data File Standards Version 1.2](https://www.google.com/search?q=arm+datastream+facility+cite+definition&rlz=1C1GCEJ_enUS1029US1029&ei=hb41ZICrBPukqtsPvMODIA&ved=0ahUKEwjAgeiS0aL-AhV7kmoFHbzhAAQQ4dUDCBA&uact=5&oq=arm+datastream+facility+cite+definition&gs_lcp=Cgxnd3Mtd2l6LXNlcnAQAzIFCAAQogQyBQgAEKIEMgUIABCiBDIFCAAQogQyBQgAEKIEOgoIABBHENYEELADOgoIIRCgARDDBBAKSgQIQRgAUJ4DWIoOYO0PaAFwAXgAgAGMAYgBuQmSAQMyLjmYAQCgAQHIAQjAAQE&sclient=gws-wiz-serp).) \n", + "\n", + "For example, this notebook is called `aosnephdry.c1`, where `aosnephdry` is the \"datastream name\", and the `{process.ds_class_level}}` is the \"data level\".\n", + "\n", + "This datastream also contains site `pvc` and facility `M1`. (Note: individual datastream might have multiple site-facility pairs.)\n", + "In such a case, the data of this data-stream is stored at `/data/archive/pvc/pvcaosnephdryM1.c1`, which is in the format of `//.`. We can use the following method to assign the data-stream directory `datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )`\n", + "\n", + "The data files under datastream_dir also follows naming conventions. But once reach the datastream_dir level, the most import file naming convention to differentiate the files is \"yyyyMMdd.hhmmss\", which comes handy to filter out files based on datetime. For example, we can use `glob.glob(f'{datastream_dir}/*.200709*.*')` to filter files in 2007 September.\n", + "\n", + "(Note: refer to the https://adc.arm.gov/discovery/#/ and https://adc.arm.gov/solr8/metadata/select API to explore ARM datastream and assoicated available sites and facilities\n", + "\n", + "Please see the following examples in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d7e9eb85", + "metadata": {}, + "outputs": [], + "source": [ + "# Verify if DATA_DIR path exists\n", + "DATA_DIR = \"/data/archive\"\n", + "os.path.exists(DATA_DIR)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "586993fd", + "metadata": {}, + "outputs": [], + "source": [ + "# Speicify datastream_dir following the path conventions and check its existence\n", + "DATASTREAM_NAME = \"aosnephdry\"\n", + "DATA_LEVEL = \"c1\"\n", + "site = \"pvc\"\n", + "facility = \"M1\"\n", + "datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )\n", + "print(datastream_dir)\n", + "print(os.path.exists(datastream_dir))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0742f7c1", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: list 5 (random) files under datastream_dir\n", + "files = os.listdir(datastream_dir)\n", + "files[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39b98a36", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: get most recent file\n", + "list_of_files = glob.glob(f\"{datastream_dir}/*\") # * means all if need specific format then *.csv\n", + "latest_file = max(list_of_files, key=os.path.getctime)\n", + "latest_file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "902d514e", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus sort datastream files based on datetime\n", + "files = os.listdir(datastream_dir)\n", + "file_sorted = files.copy()\n", + "file_sorted.sort() \n", + "print(file_sorted[:5])\n", + "\n", + "# to reverse\n", + "file_sorted_reverse = files.copy() \n", + "file_sorted_reverse.sort(reverse=True)\n", + "print(file_sorted_reverse[:5])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec5923b2", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: pattern matching\n", + "# filter the 200709** files under datastream_dir\n", + "files_filter = glob.glob(f'{datastream_dir}/*.200709*.*')\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c4a4aa18", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: use armnotebook_utils.file_filter (TODO) to filter files based on datastream info \n" + ] + }, + { + "cell_type": "markdown", + "id": "cfeb9efc", + "metadata": {}, + "source": [ + "### Load data\n", + "The arm data is stored in [NetCDF](https://en.wikipedia.org/wiki/NetCDF#:~:text=%22NetCDF%20(network%20Common%20Data%20Form,format%20for%20representing%20scientific%20data.) format. We can use xarray's `open_dataset` method to load single file, or `open_mfdataset` to open multiple files. (Note: the latter will still return a single xarray dataset object by combining multiple files.)\n", + "\n", + "Note: open_dataset keeps the file handle open and lazy loads its contents. All parameters are passed directly to open_dataset. It is a preferable method over load_dataset for memory-effiency. (more details, please see [xarray.open_dataset](https://docs.xarray.dev/en/stable/generated/xarray.open_dataset.html))\n", + "\n", + "See the following example in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a440a329", + "metadata": {}, + "outputs": [], + "source": [ + "# open single file\n", + "full_path = latest_file\n", + "print(full_path)\n", + "ds_single = xr.open_dataset(full_path)\n", + "ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0143a3d", + "metadata": {}, + "outputs": [], + "source": [ + "print(ds_single)\n", + "print(type(ds_single))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c0f8939", + "metadata": {}, + "outputs": [], + "source": [ + "# open multiple files\n", + "n_files = 3\n", + "full_paths = [os.path.join(datastream_dir, f_path) for f_path in file_sorted_reverse[:n_files]]\n", + "print(full_paths)\n", + "try: # Note: sometimes multiple files cannot be merged, so we used try except here.\n", + " ds_mutiple = xr.open_mfdataset(full_paths)\n", + " # ds_mutiple\n", + " print(type(ds_mutiple))\n", + " print(ds_mutiple)\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "3fa59943", + "metadata": {}, + "source": [ + "## Explore the data" + ] + }, + { + "cell_type": "markdown", + "id": "571f69b9", + "metadata": {}, + "source": [ + "### NetCDF Data structure \n", + "\n", + "\n", + "(If you are confident with NetCDF basics and xarray essentials, feel free to skip this session.)\n", + "Before we dive into data exploration, there are some eseential concpets we should be familiar with (shown below): \n", + "* Dataset\n", + "* Data array\n", + "* Variable\n", + "* Dimenssion\n", + "* Coordinate\n", + "* Data Type\n", + "* Meta Data (Attributes)\n", + "\n", + "We will not go into details about NetCDF basics and here are some references you might find helpful\n", + "* [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#)\n", + "* [Network Common Data Form (NetCDF)](https://www.unidata.ucar.edu/software/netcdf/) and [A Brief History of (netCDF) Time](https://www.unidata.ucar.edu/software/netcdf/time/recs.html)\n", + "\n", + "\n", + "\n", + "### Xarray essentials\n", + "Earlier we introduced the xarray pacakge and used `open_dataset` and `open_mfdataset` to retrieve NetCDF data file as an xarray Dataset object. Recall that you can review the data in a notebook by using `print(ds)` or `ds`. \n", + "\n", + "Using xarray to retrieve the aforementioned NetCDF basics is straightforwared. In general,\n", + "* Dataset: `ds`\n", + "* Data array: `ds.variables`\n", + "* Variable: `ds.ds.variables`\n", + "* Dimenssion: `ds.dims`\n", + "* Coordinate: `ds.coords`\n", + "* Data Type: `type`\n", + "* Meta Data (Attributes)\n", + "\n", + "Also, here are some references if you are new to xarray\n", + "* [Xarray in 45 minutes](https://tutorial.xarray.dev/overview/xarray-in-45-min.html)\n", + "* [Handling NetCDF Files using XArray for Absolute Beginners](https://towardsdatascience.com/handling-netcdf-files-using-xarray-for-absolute-beginners-111a8ab4463f)\n", + "\n", + "Try the following commands in action\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b3eac323", + "metadata": {}, + "outputs": [], + "source": [ + "ds = ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77ecf85d", + "metadata": {}, + "outputs": [], + "source": [ + "# Dataset \n", + "ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25e7de09", + "metadata": {}, + "outputs": [], + "source": [ + "# Data array, Variable\n", + "# Note: the info can be overwhelming. Do not attempt to grasp everything at the first glance.\n", + "ds.variables" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c41b67e", + "metadata": {}, + "outputs": [], + "source": [ + "# dimenssions\n", + "ds.dims" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "156f1dfc", + "metadata": {}, + "outputs": [], + "source": [ + "# coordinates\n", + "ds.coords" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "277d6064", + "metadata": {}, + "outputs": [], + "source": [ + "# Meta Data (Attributes)\n", + "ds.attrs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d334681f", + "metadata": {}, + "outputs": [], + "source": [ + "# type\n", + "print(type(ds))\n", + "print(type(ds.variables))\n", + "print(type(ds.dims))\n", + "print(type(ds.coords))\n", + "print(type(ds.attrs))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "643399d6", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: retrieve variable names only\n", + "list(ds.variables)" + ] + }, + { + "cell_type": "markdown", + "id": "1cf36878", + "metadata": {}, + "source": [ + "#### Discussion: variable vs. coordinates vs. dimenssions. \n", + "From [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#):\n", + "\n", + "* Dimession: A dimension may be used to represent a real physical dimension, for example, time, latitude, longitude, or height. A dimension might also be used to index other quantities, for example station or model-run-number.\n", + "A netCDF dimension has both a name and a length. A dimension length is an arbitrary positive integer, except that one dimension in a netCDF dataset can have the length UNLIMITED.\n", + "\n", + "* Variables: Variables are used to store the bulk of the data in a netCDF dataset. A variable represents an array of values of the same type. A scalar value is treated as a 0-dimensional array. A variable has a name, a data type, and a shape described by its list of dimensions specified when the variable is created. A variable may also have associated attributes, which may be added, deleted or changed after the variable is created.\n", + "\n", + "* Coordinate (Variables): It is legal for a variable to have the same name as a dimension. Such variables have no special meaning to the netCDF library. However there is a convention that such variables should be treated in a special way by software using this library.\n", + "A variable with the same name as a dimension is called a coordinate variable. It typically defines a physical coordinate corresponding to that dimension.\n", + "\n", + "\n", + "Tips: By definition, coordinates and dimenssions are also variable. For example ds.time is a coordinate but it is also a special variable. But in practice, when we talk about variable, it implies regular/non-coordinate variable. We will use this convention for the remaining notebook. " + ] + }, + { + "cell_type": "markdown", + "id": "d47120f9", + "metadata": {}, + "source": [ + "### Xarray Variable\n", + "\n", + "\n", + "We can use `ds.variables` to access varialbes of a dataset. But many times viewing print-out the whole datasets can be overwhemling. Instead, we would work on the individual variable (also called Xarray data array.) using the `ds[var_name]` syntax, where `var_name` is the name of the variable. See the following examples in action.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5a57e136", + "metadata": {}, + "outputs": [], + "source": [ + "print(type(ds[\"time\"]))\n", + "ds[\"time\"]" + ] + }, + { + "cell_type": "markdown", + "id": "51ec643e", + "metadata": {}, + "source": [ + "#### Variable properties\n", + "For individual varible, the following properties are mostly used with the assocaited xarray method to retrieve them. (assuming var = ds[var_name])\n", + "* name: `var.name`\n", + "* data content: `var.data`\n", + "* attributes: `var.attrs`\n", + "* dimenstions: `var.dims`\n", + "* data type: `var.data.dtype`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a632a525", + "metadata": {}, + "outputs": [], + "source": [ + "var_name = \"time\"\n", + "var = ds[var_name]\n", + "\n", + "print(\"var.name: \\n\", var.name, \"\\n\")\n", + "print(\"var.data: \\n\", var.data, \"\\n\")\n", + "print(\"var.attrs: \\n\", var.attrs, \"\\n\")\n", + "print(\"var.dims: \\n\", var.dims, \"\\n\")\n", + "print(\"var.data.dtype: \\n\", var.data.dtype, \"\\n\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b256b07f", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: indivdual variable info table (collect and display variable info as a tabular format)\n", + "df_info = pd.DataFrame()\n", + "df_info[\"var_name\"] = list(ds.variables)\n", + "df_info[\"dims\"] = df_info.var_name.apply(lambda x: ds[x].dims)\n", + "df_info[\"is_dim\"] = df_info.var_name.apply(lambda x: x in ds.dims)\n", + "df_info[\"n_dim\"] = df_info.var_name.apply(lambda x: len(ds[x].dims))\n", + "df_info[\"attrs\"] = df_info.var_name.apply(lambda x: ds[x].attrs)\n", + "df_info[\"dtype\"] = df_info.var_name.apply(lambda x: ds[x].data.dtype)\n", + "df_info" + ] + }, + { + "cell_type": "markdown", + "id": "e6a36b96", + "metadata": {}, + "source": [ + "### Data cleaning/Preprocessing (skipped)\n", + "Data cleaning and preprocessing is an important stage in the the data analysis pipeline. However it will be out of the scope of this notebook. For more information about data cleaning and preprocessing basics, here are some references. \n", + "* [Xarray Fundamentals](https://earth-env-data-science.github.io/lectures/xarray/xarray.html)\n", + "* [Xarray Tutorial — Pangeo Gallery documentation](http://gallery.pangeo.io/repos/pangeo-data/pangeo-tutorial-gallery/xarray.html)\n", + "* [Pythonic Data Cleaning With pandas and NumPy](https://realpython.com/python-data-cleaning-numpy-pandas/)\n", + "* [Pandas - Cleaning Data](https://www.w3schools.com/python/pandas/pandas_cleaning.asp)" + ] + }, + { + "cell_type": "markdown", + "id": "ae6e1a69", + "metadata": {}, + "source": [ + "### Plotting\n", + "Data visualization (or plotting) is another important data analysis topic and deserves its own discussion. Here in this notebook we will only demonstrate basic tool for simple data visualization tasks.\n", + "\n", + "Here are some reference you might find useful:\n", + "* xarray plotting: https://docs.xarray.dev/en/stable/user-guide/plotting.html\n", + "* Atmospheric Community Toolkit (ACT): https://arm-doe.github.io/ACT/index.html\n", + "\n", + "Note: this notebook is auto-generated using a template. It uses a general idea to select variable(s) to plot and is not customized to each indivdual notebook. Feel free to change the variables in intrrests, especially certain figure is failed to plot." + ] + }, + { + "cell_type": "markdown", + "id": "7e417fb2", + "metadata": {}, + "source": [ + "#### 1-dimenssional basic time series plot\n", + "\n", + "For the following plot we would like to find variables such that\n", + "* it has one and only one dimession\n", + "* \"time\" is its coordinate variable\n", + "* it is not a dimenssion itself,\n", + "* it is not a special variable with substrings within [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "\n", + "Please see the following example in action. (For more details about pandas filtering, please see the following references.)\n", + "* [pandas: multiple conditions while indexing data frame](https://stackoverflow.com/questions/22591174/pandas-multiple-conditions-while-indexing-data-frame-unexpected-behavior)\n", + "* [pandas.DataFrame.apply](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.apply.html)\n", + "* [How to test if a string contains one of the substrings in a list...](https://stackoverflow.com/questions/26577516/how-to-test-if-a-string-contains-one-of-the-substrings-in-a-list-in-pandas" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30edac2d", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==1, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter = df_info[(df_info.n_dim == 1) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4777a659", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_1d = df_filter.var_name.values[0]\n", + " var_1d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb5ae985", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " ds[var_1d].plot()\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "9e41066e", + "metadata": {}, + "source": [ + "#### 2-dimenssional basic plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09bd4adc", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==2, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_2 = df_info[(df_info.n_dim == 2) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2cdea1a9", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_2d = df_filter_2.var_name.values[0]\n", + " var_2d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5f48879", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " print(ds[var_2d].dims)\n", + " # ds[var_2d].plot()\n", + "\n", + " # conventionally, use \"time\" as x-axis\n", + " ds[var_2d].plot(x=\"time\")\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "7554b2a6", + "metadata": {}, + "source": [ + "#### qc-plotting (optional)\n", + "\n", + "Note: act qc plotting has more strict requirement, one of them is the associated qc variable need to have \"flag_masks\" attributes. Which is added by using ds = act.io.armfiles.read_netcdf(files_list), then ds.clean.cleanup()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71c2096f", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " ds_act = act.io.armfiles.read_netcdf(full_path)\n", + " print(type(ds_act))\n", + " ds_act.clean.cleanup()\n", + "\n", + " # or \n", + " # ds.clean.cleanup()\n", + "except Exception as e:\n", + " print(\"ERROR\", e)\n", + " ds_act = ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "844f8505", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter valid variables for ACT qc plotting\n", + "condition = (\"qc_\" + df_info.var_name).apply(lambda x: ds_act[x].attrs.get(\"flag_masks\") is not None \n", + " if x in list(ds.variables) else False)\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_3 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings))) &\n", + " condition\n", + " ]\n", + "df_filter_3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd38cc6", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "try:\n", + " qc_variable = df_filter_3.var_name.values[0]\n", + " print(qc_variable)\n", + "except Exception as e:\n", + " print(e)\n", + " \n", + "try:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + " qc_ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "d76e4d27", + "metadata": {}, + "source": [ + "#### bonus: choose variables to plot from a dropdown menu " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb733804", + "metadata": {}, + "outputs": [], + "source": [ + "# Valid variables filtering\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_4 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82d5c20d", + "metadata": {}, + "outputs": [], + "source": [ + "# example 1: using xarray plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2fc9aaa4", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# fig, ax = plt.subplots(figsize=(10, 4))\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "# fig.clear() # Remove old lines from plot and plot new one\n", + "# if len(ds[var].dims)==2:\n", + "# ds[var].plot(x=\"time\", add_colorbar=False)\n", + "# else:\n", + "# ds[var].plot()\n", + "# plt.grid()\n", + "# plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fabdd802", + "metadata": {}, + "outputs": [], + "source": [ + "# example 2: using act plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "962b4186", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "\n", + "\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "\n", + "# i_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + "# i_display.add_subplots((1,), figsize=(10, 4))\n", + "# ax = i_display.plot(var, subplot_index=(0,), set_title=f\"{var} ({ds_act[var].attrs['long_name']})\",)\n", + "\n", + "# ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "# ax.grid()\n", + "# plt.show()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/NEPHELOMETER/aosnephdry.c1.ipynb b/VAPs/quicklook/NEPHELOMETER/aosnephdry.c1.ipynb new file mode 100644 index 00000000..67145537 --- /dev/null +++ b/VAPs/quicklook/NEPHELOMETER/aosnephdry.c1.ipynb @@ -0,0 +1,339 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# AOSNEPHDRY.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/nephelometer) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'aosnephdry'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2013-06-24', 'facility': 'M1', 'site': 'pvc', 'start_date': '2012-07-16'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'pvc', 'M1' )\n", + "\n", + "date_start = '2013-06-22'\n", + "date_end = '2013-06-24'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['Bs_B_Dry_Neph3W_1', 'Bs_G_Dry_Neph3W_1', 'Bs_R_Dry_Neph3W_1']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'Bs_B_Dry_Neph3W_1'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'Bs_B_Dry_Neph3W_1'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/NEPHELOMETER/aosnephwet.c1.ipynb b/VAPs/quicklook/NEPHELOMETER/aosnephwet.c1.ipynb new file mode 100644 index 00000000..44b1a62f --- /dev/null +++ b/VAPs/quicklook/NEPHELOMETER/aosnephwet.c1.ipynb @@ -0,0 +1,339 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# AOSNEPHWET.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/nephelometer) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'aosnephwet'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2013-06-24', 'facility': 'M1', 'site': 'pvc', 'start_date': '2012-07-16'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'pvc', 'M1' )\n", + "\n", + "date_start = '2013-06-22'\n", + "date_end = '2013-06-24'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['Bs_B_Wet_Neph3W_2', 'Bs_G_Wet_Neph3W_2', 'Bs_R_Wet_Neph3W_2']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'Bs_B_Wet_Neph3W_2'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'Bs_B_Wet_Neph3W_2'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/OKMSOIL/.ipynb_checkpoints/okmsoil.c1-checkpoint.ipynb b/VAPs/quicklook/OKMSOIL/.ipynb_checkpoints/okmsoil.c1-checkpoint.ipynb new file mode 100644 index 00000000..b2aa8667 --- /dev/null +++ b/VAPs/quicklook/OKMSOIL/.ipynb_checkpoints/okmsoil.c1-checkpoint.ipynb @@ -0,0 +1,2048 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# OKMSOIL.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/okmsoil) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'okmsoil'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2020-10-22', 'facility': 'X1', 'site': 'sgp', 'start_date': '1998-01-01'}]" + ] + }, + { + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "ac6764f5", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The following locations and date ranges are available for this VAP:\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
sitefacilitystart_dateend_date
0sgpX11998-01-012020-10-22
\n", + "
" + ], + "text/plain": [ + " site facility start_date end_date\n", + "0 sgp X1 1998-01-01 2020-10-22" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'X1' )\n", + "\n", + "date_start = '2020-10-20'\n", + "date_end = '2020-10-22'" + ] + }, + { + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'/data/archive/sgp/sgpokmsoilX1.c1'" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['20201020', '20201021', '20201022']" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "51feea2e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['/data/archive/sgp/sgpokmsoilX1.c1/sgpokmsoilX1.c1.20201020.000000.nc',\n", + " '/data/archive/sgp/sgpokmsoilX1.c1/sgpokmsoilX1.c1.20201021.000000.nc',\n", + " '/data/archive/sgp/sgpokmsoilX1.c1/sgpokmsoilX1.c1.20201022.000000.nc']" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "3 files loaded\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
<xarray.Dataset>\n",
+       "Dimensions:                      (time: 144, bound: 2, station_number: 133,\n",
+       "                                  depth: 3)\n",
+       "Coordinates:\n",
+       "  * time                         (time) datetime64[ns] 2020-10-20 ... 2020-10...\n",
+       "  * station_number               (station_number) float32 110.0 1.0 ... 108.0\n",
+       "  * depth                        (depth) int32 5 25 60\n",
+       "Dimensions without coordinates: bound\n",
+       "Data variables: (12/15)\n",
+       "    base_time                    (time) datetime64[ns] 2020-10-20 ... 2020-10-22\n",
+       "    time_offset                  (time) datetime64[ns] 2020-10-20 ... 2020-10...\n",
+       "    time_bounds                  (time, bound) object dask.array<chunksize=(48, 2), meta=np.ndarray>\n",
+       "    sensor_temperature_rise      (time, depth, station_number) float32 dask.array<chunksize=(48, 3, 133), meta=np.ndarray>\n",
+       "    qc_sensor_temperature_rise   (time, depth, station_number) int32 dask.array<chunksize=(48, 3, 133), meta=np.ndarray>\n",
+       "    matric_potential             (time, depth, station_number) float32 dask.array<chunksize=(48, 3, 133), meta=np.ndarray>\n",
+       "    ...                           ...\n",
+       "    fractional_water_index       (time, depth, station_number) float32 dask.array<chunksize=(48, 3, 133), meta=np.ndarray>\n",
+       "    qc_fractional_water_index    (time, depth, station_number) int32 dask.array<chunksize=(48, 3, 133), meta=np.ndarray>\n",
+       "    station                      (time, station_number) |S20 dask.array<chunksize=(48, 133), meta=np.ndarray>\n",
+       "    lat                          (time, station_number) float32 dask.array<chunksize=(48, 133), meta=np.ndarray>\n",
+       "    lon                          (time, station_number) float32 dask.array<chunksize=(48, 133), meta=np.ndarray>\n",
+       "    alt                          (time, station_number) float32 dask.array<chunksize=(48, 133), meta=np.ndarray>\n",
+       "Attributes: (12/53)\n",
+       "    Version:                          $State: xdc-sgp30okm-9.0-1.el5 $\n",
+       "    command_line:                     idl -R -n okmsoil -s sgp -f X1 -b 20201...\n",
+       "    dod_version:                      okmsoil-c1-1.0\n",
+       "    date:                             \n",
+       "    process_version:                  vap-okmsoil-1.0-1.el7\n",
+       "    idl_version:                      \n",
+       "    ...                               ...\n",
+       "    doi:                              10.5439/1432043\n",
+       "    history:                          created by user dsmgr on machine flint ...\n",
+       "    _file_dates:                      ['20201020', '20201021', '20201022']\n",
+       "    _file_times:                      ['000000', '000000', '000000']\n",
+       "    _datastream:                      sgpokmsoilX1.c1\n",
+       "    _arm_standards_flag:              1
" + ], + "text/plain": [ + "\n", + "Dimensions: (time: 144, bound: 2, station_number: 133,\n", + " depth: 3)\n", + "Coordinates:\n", + " * time (time) datetime64[ns] 2020-10-20 ... 2020-10...\n", + " * station_number (station_number) float32 110.0 1.0 ... 108.0\n", + " * depth (depth) int32 5 25 60\n", + "Dimensions without coordinates: bound\n", + "Data variables: (12/15)\n", + " base_time (time) datetime64[ns] 2020-10-20 ... 2020-10-22\n", + " time_offset (time) datetime64[ns] 2020-10-20 ... 2020-10...\n", + " time_bounds (time, bound) object dask.array\n", + " sensor_temperature_rise (time, depth, station_number) float32 dask.array\n", + " qc_sensor_temperature_rise (time, depth, station_number) int32 dask.array\n", + " matric_potential (time, depth, station_number) float32 dask.array\n", + " ... ...\n", + " fractional_water_index (time, depth, station_number) float32 dask.array\n", + " qc_fractional_water_index (time, depth, station_number) int32 dask.array\n", + " station (time, station_number) |S20 dask.array\n", + " lat (time, station_number) float32 dask.array\n", + " lon (time, station_number) float32 dask.array\n", + " alt (time, station_number) float32 dask.array\n", + "Attributes: (12/53)\n", + " Version: $State: xdc-sgp30okm-9.0-1.el5 $\n", + " command_line: idl -R -n okmsoil -s sgp -f X1 -b 20201...\n", + " dod_version: okmsoil-c1-1.0\n", + " date: \n", + " process_version: vap-okmsoil-1.0-1.el7\n", + " idl_version: \n", + " ... ...\n", + " doi: 10.5439/1432043\n", + " history: created by user dsmgr on machine flint ...\n", + " _file_dates: ['20201020', '20201021', '20201022']\n", + " _file_times: ['000000', '000000', '000000']\n", + " _datastream: sgpokmsoilX1.c1\n", + " _arm_standards_flag: 1" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['sensor_temperature_rise', 'matric_potential', 'volumetric_water_content']" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [ + { + "ename": "TypeError", + "evalue": "Dimensions of C (133, 3, 144) should be one smaller than X(144) and Y(3) while using shading='flat' see help(pcolormesh)", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[0;32mIn[9], line 5\u001b[0m\n\u001b[1;32m 2\u001b[0m ts_display\u001b[38;5;241m.\u001b[39madd_subplots((\u001b[38;5;28mlen\u001b[39m(variables_to_plot),), figsize \u001b[38;5;241m=\u001b[39m (\u001b[38;5;241m9.5\u001b[39m,\u001b[38;5;241m4\u001b[39m\u001b[38;5;241m*\u001b[39m\u001b[38;5;28mlen\u001b[39m(variables_to_plot)))\n\u001b[1;32m 4\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m i,v \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28menumerate\u001b[39m(variables_to_plot):\n\u001b[0;32m----> 5\u001b[0m ts_ax \u001b[38;5;241m=\u001b[39m \u001b[43mts_display\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mplot\u001b[49m\u001b[43m(\u001b[49m\u001b[43mv\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43msubplot_index\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mi\u001b[49m\u001b[43m,\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mset_title\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mds\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mvariables\u001b[49m\u001b[43m[\u001b[49m\u001b[43mv\u001b[49m\u001b[43m]\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mattrs\u001b[49m\u001b[43m[\u001b[49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43mlong_name\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m,\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 6\u001b[0m ts_ax\u001b[38;5;241m.\u001b[39mgrid()\n\u001b[1;32m 8\u001b[0m plt\u001b[38;5;241m.\u001b[39mshow()\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/act/plotting/timeseriesdisplay.py:588\u001b[0m, in \u001b[0;36mTimeSeriesDisplay.plot\u001b[0;34m(self, field, dsname, subplot_index, cmap, set_title, add_nan, day_night_background, invert_y_axis, abs_limits, time_rng, y_rng, use_var_for_y, set_shading, assessment_overplot, overplot_marker, overplot_behind, overplot_markersize, assessment_overplot_category, assessment_overplot_category_color, force_line_plot, labels, cbar_label, cbar_h_adjust, secondary_y, y_axis_flag_meanings, colorbar_labels, cb_friendly, **kwargs)\u001b[0m\n\u001b[1;32m 586\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124medgecolors\u001b[39m\u001b[38;5;124m'\u001b[39m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;129;01min\u001b[39;00m kwargs\u001b[38;5;241m.\u001b[39mkeys():\n\u001b[1;32m 587\u001b[0m kwargs[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124medgecolors\u001b[39m\u001b[38;5;124m'\u001b[39m] \u001b[38;5;241m=\u001b[39m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mface\u001b[39m\u001b[38;5;124m'\u001b[39m\n\u001b[0;32m--> 588\u001b[0m mesh \u001b[38;5;241m=\u001b[39m \u001b[43max\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mpcolormesh\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 589\u001b[0m \u001b[43m \u001b[49m\u001b[43mnp\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43masarray\u001b[49m\u001b[43m(\u001b[49m\u001b[43mxdata\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 590\u001b[0m \u001b[43m \u001b[49m\u001b[43mydata\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 591\u001b[0m \u001b[43m \u001b[49m\u001b[43mdata\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mtranspose\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 592\u001b[0m \u001b[43m \u001b[49m\u001b[43mshading\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mset_shading\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 593\u001b[0m \u001b[43m \u001b[49m\u001b[43mcmap\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcmap\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 594\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 595\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 597\u001b[0m \u001b[38;5;66;03m# Set Title\u001b[39;00m\n\u001b[1;32m 598\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m set_title \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/matplotlib/__init__.py:1442\u001b[0m, in \u001b[0;36m_preprocess_data..inner\u001b[0;34m(ax, data, *args, **kwargs)\u001b[0m\n\u001b[1;32m 1439\u001b[0m \u001b[38;5;129m@functools\u001b[39m\u001b[38;5;241m.\u001b[39mwraps(func)\n\u001b[1;32m 1440\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21minner\u001b[39m(ax, \u001b[38;5;241m*\u001b[39margs, data\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mNone\u001b[39;00m, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs):\n\u001b[1;32m 1441\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m data \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[0;32m-> 1442\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mfunc\u001b[49m\u001b[43m(\u001b[49m\u001b[43max\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;28;43mmap\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43msanitize_sequence\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43margs\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 1444\u001b[0m bound \u001b[38;5;241m=\u001b[39m new_sig\u001b[38;5;241m.\u001b[39mbind(ax, \u001b[38;5;241m*\u001b[39margs, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs)\n\u001b[1;32m 1445\u001b[0m auto_label \u001b[38;5;241m=\u001b[39m (bound\u001b[38;5;241m.\u001b[39marguments\u001b[38;5;241m.\u001b[39mget(label_namer)\n\u001b[1;32m 1446\u001b[0m \u001b[38;5;129;01mor\u001b[39;00m bound\u001b[38;5;241m.\u001b[39mkwargs\u001b[38;5;241m.\u001b[39mget(label_namer))\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/matplotlib/axes/_axes.py:6220\u001b[0m, in \u001b[0;36mAxes.pcolormesh\u001b[0;34m(self, alpha, norm, cmap, vmin, vmax, shading, antialiased, *args, **kwargs)\u001b[0m\n\u001b[1;32m 6217\u001b[0m shading \u001b[38;5;241m=\u001b[39m shading\u001b[38;5;241m.\u001b[39mlower()\n\u001b[1;32m 6218\u001b[0m kwargs\u001b[38;5;241m.\u001b[39msetdefault(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124medgecolors\u001b[39m\u001b[38;5;124m'\u001b[39m, \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mnone\u001b[39m\u001b[38;5;124m'\u001b[39m)\n\u001b[0;32m-> 6220\u001b[0m X, Y, C, shading \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_pcolorargs\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43mpcolormesh\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 6221\u001b[0m \u001b[43m \u001b[49m\u001b[43mshading\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mshading\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mkwargs\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 6222\u001b[0m coords \u001b[38;5;241m=\u001b[39m np\u001b[38;5;241m.\u001b[39mstack([X, Y], axis\u001b[38;5;241m=\u001b[39m\u001b[38;5;241m-\u001b[39m\u001b[38;5;241m1\u001b[39m)\n\u001b[1;32m 6223\u001b[0m \u001b[38;5;66;03m# convert to one dimensional array, except for 3D RGB(A) arrays\u001b[39;00m\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/matplotlib/axes/_axes.py:5751\u001b[0m, in \u001b[0;36mAxes._pcolorargs\u001b[0;34m(self, funcname, shading, *args, **kwargs)\u001b[0m\n\u001b[1;32m 5749\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m shading \u001b[38;5;241m==\u001b[39m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mflat\u001b[39m\u001b[38;5;124m'\u001b[39m:\n\u001b[1;32m 5750\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m (Nx, Ny) \u001b[38;5;241m!=\u001b[39m (ncols \u001b[38;5;241m+\u001b[39m \u001b[38;5;241m1\u001b[39m, nrows \u001b[38;5;241m+\u001b[39m \u001b[38;5;241m1\u001b[39m):\n\u001b[0;32m-> 5751\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mTypeError\u001b[39;00m(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mDimensions of C \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mC\u001b[38;5;241m.\u001b[39mshape\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m should\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 5752\u001b[0m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m be one smaller than X(\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mNx\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m) and Y(\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mNy\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m)\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 5753\u001b[0m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m while using shading=\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mflat\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 5754\u001b[0m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m see help(\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mfuncname\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m)\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[1;32m 5755\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m: \u001b[38;5;66;03m# ['nearest', 'gouraud']:\u001b[39;00m\n\u001b[1;32m 5756\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m (Nx, Ny) \u001b[38;5;241m!=\u001b[39m (ncols, nrows):\n", + "\u001b[0;31mTypeError\u001b[0m: Dimensions of C (133, 3, 144) should be one smaller than X(144) and Y(3) while using shading='flat' see help(pcolormesh)" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "dc399e8005384c0e9f15675a731f43f7", + "version_major": 2, + "version_minor": 0 + }, + "image/png": "iVBORw0KGgoAAAANSUhEUgAAA7YAAASwCAYAAADPBNYLAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAA9hAAAPYQGoP6dpAABi0UlEQVR4nO3df3RV5Z3o/08gkKA1sUAJIIjRaqXlqkO4IrF8Ha3GQceWGeeKY5eog/c2Vy0DqV5FZvmD5axMO6tO6w9Qr6DjXWgz/hxmbkbNzFhFwRlJg+MIrb1CDWgiTRwT1DYI7O8fLjJNE5Rf5yQPvF5rnT/Ow7OTZ283cb/Z5+QUZFmWBQAAACRqUH8vAAAAAPaHsAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAnbHHvhhRfiggsuiLFjx0ZBQUE89dRTn7nN888/HxUVFVFcXBzHHnts3HPPPblfKAAAQKKEbY59+OGHcfLJJ8ddd921R/M3btwY5513XkyfPj2amprixhtvjLlz58bjjz+e45UCAACkqSDLsqy/F3GoKCgoiCeffDJmzpy52znXX399rFixItavX989Vl1dHa+++mqsXr06D6sEAABIS2F/L4CeVq9eHVVVVT3Gzj333Fi6dGl8/PHHMWTIkD636+rqiq6uru7nO3fujPfeey9GjBgRBQUFOV0zAAAc6rIsi61bt8bYsWNj0CAvjM03YTvAtLa2RllZWY+xsrKy2L59e7S1tcWYMWP63K62tjZuvfXWfCwRAADYjU2bNsW4ceP6exmHHGE7AP32HdZdrxb/tDuvCxYsiJqamu7nHR0dcfTRR8emTZuipKQkNwsFAAAiIqKzszPGjx8fRxxxRH8v5ZAkbAeY0aNHR2tra4+xLVu2RGFhYYwYMWK32xUVFUVRUVGv8ZKSEmELAAB54m2A/cOLvweYadOmRUNDQ4+xZ599NqZMmbLb99cCAAAcyoRtjn3wwQexdu3aWLt2bUR88nE+a9eujebm5oj45CXEs2fP7p5fXV0db731VtTU1MT69etj2bJlsXTp0rj22mv7Y/kAAAADnpci59iaNWvizDPP7H6+632wl112WTz44IPR0tLSHbkREeXl5VFfXx/z58+Pu+++O8aOHRt33HFHXHjhhXlfOwAAQAp8ju1BqrOzM0pLS6Ojo8N7bAEAIMdcf/cvL0UGAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbPNk8eLFUV5eHsXFxVFRURErV6781PnLly+Pk08+OQ477LAYM2ZMXHHFFdHe3p6n1QIAAKRD2OZBXV1dzJs3LxYuXBhNTU0xffr0mDFjRjQ3N/c5/8UXX4zZs2fHnDlz4vXXX49HH300XnnllbjyyivzvHIAAICBT9jmwe233x5z5syJK6+8MiZOnBg/+MEPYvz48bFkyZI+57/88stxzDHHxNy5c6O8vDy++tWvxre+9a1Ys2ZNnlcOAAAw8AnbHNu2bVs0NjZGVVVVj/GqqqpYtWpVn9tUVlbG5s2bo76+PrIsi3fffTcee+yxOP/883f7fbq6uqKzs7PHAwAA4FAgbHOsra0tduzYEWVlZT3Gy8rKorW1tc9tKisrY/ny5TFr1qwYOnRojB49Oo488si48847d/t9amtro7S0tPsxfvz4A7ofAAAAA5WwzZOCgoIez7Ms6zW2y7p162Lu3Llx0003RWNjYzz99NOxcePGqK6u3u3XX7BgQXR0dHQ/Nm3adEDXDwAAMFAV9vcCDnYjR46MwYMH97o7u2XLll53cXepra2N008/Pa677rqIiDjppJPi8MMPj+nTp8dtt90WY8aM6bVNUVFRFBUVHfgdAAAAGODcsc2xoUOHRkVFRTQ0NPQYb2hoiMrKyj63+eijj2LQoJ7/aQYPHhwRn9zpBQAA4D8J2zyoqamJ+++/P5YtWxbr16+P+fPnR3Nzc/dLixcsWBCzZ8/unn/BBRfEE088EUuWLIkNGzbESy+9FHPnzo1TTz01xo4d21+7AQAAMCB5KXIezJo1K9rb22PRokXR0tISkyZNivr6+pgwYUJERLS0tPT4TNvLL788tm7dGnfddVd85zvfiSOPPDLOOuus+O53v9tfuwAAADBgFWRe23pQ6uzsjNLS0ujo6IiSkpL+Xg4AABzUXH/3Ly9FBgAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmzzZPHixVFeXh7FxcVRUVERK1eu/NT5XV1dsXDhwpgwYUIUFRXFcccdF8uWLcvTagEAANJR2N8LOBTU1dXFvHnzYvHixXH66afHvffeGzNmzIh169bF0Ucf3ec2F110Ubz77ruxdOnS+OIXvxhbtmyJ7du353nlAAAAA19BlmVZfy/iYDd16tSYPHlyLFmypHts4sSJMXPmzKitre01/+mnn46LL744NmzYEMOHD9+n79nZ2RmlpaXR0dERJSUl+7x2AADgs7n+7l9eipxj27Zti8bGxqiqquoxXlVVFatWrepzmxUrVsSUKVPie9/7Xhx11FFxwgknxLXXXhu/+tWv8rFkAACApHgpco61tbXFjh07oqysrMd4WVlZtLa29rnNhg0b4sUXX4zi4uJ48skno62tLa666qp47733dvs+266urujq6up+3tnZeeB2AgAAYABzxzZPCgoKejzPsqzX2C47d+6MgoKCWL58eZx66qlx3nnnxe233x4PPvjgbu/a1tbWRmlpafdj/PjxB3wfAAAABiJhm2MjR46MwYMH97o7u2XLll53cXcZM2ZMHHXUUVFaWto9NnHixMiyLDZv3tznNgsWLIiOjo7ux6ZNmw7cTgAAAAxgwjbHhg4dGhUVFdHQ0NBjvKGhISorK/vc5vTTT4933nknPvjgg+6xN954IwYNGhTjxo3rc5uioqIoKSnp8QAAADgUCNs8qKmpifvvvz+WLVsW69evj/nz50dzc3NUV1dHxCd3W2fPnt09/5JLLokRI0bEFVdcEevWrYsXXnghrrvuuviTP/mTGDZsWH/tBgAAwIDkl0fl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pEmTor6+PiZMmBARES0tLT0+0/bee++N7du3x9VXXx1XX3119/hll10WDz74YL6XDwAAMKD5HNuDlM/RAgCA/HH93b+8xxYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwzZPFixdHeXl5FBcXR0VFRaxcufJT5z///PNRUVERxcXFceyxx8Y999yTp5UCAACkRdjmQV1dXcybNy8WLlwYTU1NMX369JgxY0Y0Nzf3OX/jxo1x3nnnxfTp06OpqSluvPHGmDt3bjz++ON5XjkAAMDAV5BlWdbfizjYTZ06NSZPnhxLlizpHps4cWLMnDkzamtre82//vrrY8WKFbF+/fruserq6nj11Vdj9erVe/Q9Ozs7o7S0NDo6OqKkpGT/dwIAANgt19/9yx3bHNu2bVs0NjZGVVVVj/GqqqpYtWpVn9usXr261/xzzz031qxZEx9//HHO1goAAJCiwv5ewMGura0tduzYEWVlZT3Gy8rKorW1tc9tWltb+5y/ffv2aGtrizFjxvTapqurK7q6urqfd3R0RMQn/3IEAADk1q7rbi+I7R/CNk8KCgp6PM+yrNfYZ83va3yX2trauPXWW3uNjx8/fm+XCgAA7KP29vYoLS3t72UccoRtjo0cOTIGDx7c6+7sli1bet2V3WX06NF9zi8sLIwRI0b0uc2CBQuipqam+/n7778fEyZMiObmZn+xPkVnZ2eMHz8+Nm3a5L0Qu+EY7RnH6bM5RnvGcdozjtNnc4z2jOP02RyjPdPR0RFHH310DB8+vL+XckgStjk2dOjQqKioiIaGhviDP/iD7vGGhob4xje+0ec206ZNi7/7u7/rMfbss8/GlClTYsiQIX1uU1RUFEVFRb3GS0tL/QDaAyUlJY7TZ3CM9ozj9Nkcoz3jOO0Zx+mzOUZ7xnH6bI7Rnhk0yK8x6g+Oeh7U1NTE/fffH8uWLYv169fH/Pnzo7m5OaqrqyPik7uts2fP7p5fXV0db731VtTU1MT69etj2bJlsXTp0rj22mv7axcAAAAGLHds82DWrFnR3t4eixYtipaWlpg0aVLU19fHhAkTIiKipaWlx2falpeXR319fcyfPz/uvvvuGDt2bNxxxx1x4YUX9tcuAAAADFjCNk+uuuqquOqqq/r8swcffLDX2BlnnBE/+clP9vn7FRUVxc0339zny5P5T47TZ3OM9ozj9Nkcoz3jOO0Zx+mzOUZ7xnH6bI7RnnGc+ldB5vdRAwAAkDDvsQUAACBpwhYAAICkCVsAAACSJmwHqNra2viv//W/xhFHHBGjRo2KmTNnxs9+9rMec7Isi1tuuSXGjh0bw4YNi9/93d+N119/vcecrq6u+Pa3vx0jR46Mww8/PL7+9a/H5s2be8z5j//4j7j00kujtLQ0SktL49JLL433338/17t4QOTzOP35n/95VFZWxmGHHRZHHnlkrnftgMnXMfrFL34Rc+bMifLy8hg2bFgcd9xxcfPNN8e2bdvysp/7K5/n0te//vU4+uijo7i4OMaMGROXXnppvPPOOznfx/2Vz2P0m3NPOeWUKCgoiLVr1+Zq1w6ofB6nY445JgoKCno8brjhhpzv44GQ7/Pp//7f/xtTp06NYcOGxciRI+MP//APc7p/B0K+jtGPf/zjXufRrscrr7ySl33dH/k8l9544434xje+ESNHjoySkpI4/fTT47nnnsv5Ph4I+TxOP/nJT+Kcc86JI488MkaMGBH/43/8j/jggw9yvo/760Ado/vuuy9+93d/N0pKSqKgoKDP6+qUr78HrIwB6dxzz80eeOCB7N///d+ztWvXZueff3529NFHZx988EH3nL/4i7/IjjjiiOzxxx/PXnvttWzWrFnZmDFjss7Ozu451dXV2VFHHZU1NDRkP/nJT7IzzzwzO/nkk7Pt27d3z/m93/u9bNKkSdmqVauyVatWZZMmTcp+//d/P6/7u6/yeZxuuumm7Pbbb89qamqy0tLSfO7mfsnXMfqHf/iH7PLLL8+eeeaZ7M0338z+9m//Nhs1alT2ne98J+/7vC/yeS7dfvvt2erVq7Nf/OIX2UsvvZRNmzYtmzZtWl73d1/k8xjtMnfu3GzGjBlZRGRNTU352M39ls/jNGHChGzRokVZS0tL92Pr1q153d99lc/j9Nhjj2Wf//znsyVLlmQ/+9nPsp/+9KfZo48+mtf93Rf5OkZdXV09zqGWlpbsyiuvzI455phs586ded/vvZXPc+mLX/xidt5552Wvvvpq9sYbb2RXXXVVdthhh2UtLS153ed9ka/j9Pbbb2ef//zns+rq6uynP/1p9q//+q9ZZWVlduGFF+Z9n/fWgTpGf/VXf5XV1tZmtbW1WURk//Ef/9Hre6V8/T1QCdtEbNmyJYuI7Pnnn8+yLMt27tyZjR49OvuLv/iL7jm//vWvs9LS0uyee+7JsizL3n///WzIkCHZj370o+45b7/9djZo0KDs6aefzrIsy9atW5dFRPbyyy93z1m9enUWEdlPf/rTfOzaAZWr4/SbHnjggaTC9rfl4xjt8r3vfS8rLy/P0Z7kVj6P09/+7d9mBQUF2bZt23K0N7mR62NUX1+fnXjiidnrr7+eVNj+tlwepwkTJmR/9Vd/lZ8dybFcHaePP/44O+qoo7L7778/j3uTG/n6ubRt27Zs1KhR2aJFi3K4N7mTq+P0y1/+MouI7IUXXuie09nZmUVE9o//+I/52LUDKlfH6d57781GjRqV7dixo3tOU1NTFhHZz3/+83zs2gGzL8foNz333HN9hu3Bdv09UHgpciI6OjoiImL48OEREbFx48ZobW2Nqqqq7jlFRUVxxhlnxKpVqyIiorGxMT7++OMec8aOHRuTJk3qnrN69eooLS2NqVOnds857bTTorS0tHtOSnJ1nA4m+TxGHR0d3d8nNfk6Tu+9914sX748KisrY8iQIbnanZzI5TF6991347//9/8e/+f//J847LDD8rE7OZPrc+m73/1ujBgxIk455ZT48z//82Re/v/bcnWcfvKTn8Tbb78dgwYNit/5nd+JMWPGxIwZM3q9dDAF+fq5tGLFimhra4vLL788R3uSW7k6TiNGjIiJEyfGQw89FB9++GFs37497r333igrK4uKiop87d4Bk6vj1NXVFUOHDo1Bg/4zM4YNGxYRES+++GJud+oA25djtCcOtuvvgULYJiDLsqipqYmvfvWrMWnSpIiIaG1tjYiIsrKyHnPLysq6/6y1tTWGDh0an//85z91zqhRo3p9z1GjRnXPSUUuj9PBIp/H6M0334w777wzqqurD/Ru5Fw+jtP1118fhx9+eIwYMSKam5vjb//2b3O1OzmRy2OUZVlcfvnlUV1dHVOmTMn1ruRUrs+lP/3TP40f/ehH8dxzz8U111wTP/jBD+Kqq67K5S7lRC6P04YNGyIi4pZbbok/+7M/i7//+7+Pz3/+83HGGWfEe++9l9P9OpDy+fN76dKlce6558b48eMP9G7kXC6PU0FBQTQ0NERTU1McccQRUVxcHH/1V38VTz/9dFK/eyMit8fprLPOitbW1vjLv/zL2LZtW/zHf/xH3HjjjRER0dLSktP9OpD29RjtiYPp+nsgEbYJuOaaa+Lf/u3f4pFHHun1ZwUFBT2eZ1nWa+y3/facvubvydcZaHJ9nA4G+TpG77zzTvze7/1e/Lf/9t/iyiuv3L9F94N8HKfrrrsumpqa4tlnn43BgwfH7NmzI8uy/V98nuTyGN15553R2dkZCxYsOHAL7ie5Ppfmz58fZ5xxRpx00klx5ZVXxj333BNLly6N9vb2A7MDeZLL47Rz586IiFi4cGFceOGFUVFREQ888EAUFBTEo48+eoD2IPfy9fN78+bN8cwzz8ScOXP2b8H9JJfHKcuyuOqqq2LUqFGxcuXK+Nd//df4xje+Eb//+7+fVLBF5PY4feUrX4m//uu/ju9///tx2GGHxejRo+PYY4+NsrKyGDx48IHbiRw70Mfos77Gvn4d/pOwHeC+/e1vx4oVK+K5556LcePGdY+PHj06IqLXv+ps2bKl+1+RRo8e3f0vZZ8259133+31fX/5y1/2+teogSzXx+lgkK9j9M4778SZZ54Z06ZNi/vuuy8Xu5JT+TpOI0eOjBNOOCHOOeec+NGPfhT19fXx8ssv52KXDrhcH6N//ud/jpdffjmKioqisLAwvvjFL0ZExJQpU+Kyyy7L2X4daP3xc+m0006LiIj/9//+3wHZh3zI9XEaM2ZMRER8+ctf7v7zoqKiOPbYY6O5ufnA71AO5PNceuCBB2LEiBHx9a9//UDvRs7l42fT3//938ePfvSjOP3002Py5MmxePHiGDZsWPz1X/91LnftgMrH+XTJJZdEa2trvP3229He3h633HJL/PKXv4zy8vJc7dYBtT/HaE8cLNffA05O38HLPtu5c2d29dVXZ2PHjs3eeOONPv989OjR2Xe/+93usa6urj7f4F9XV9c955133unzl0f9y7/8S/ecl19+OZk3r+frOP2m1H55VD6P0ebNm7Pjjz8+u/jii/v8DbcDWX+cS7s0NzdnEZE999xzB26HciBfx+itt97KXnvtte7HM888k0VE9thjj2WbNm3K8V7uv/48l/7u7/4ui4jsrbfeOoB7lBv5Ok4dHR1ZUVFRj18eteuXI91777252r0DIt/n0s6dO7Py8vJkfpv9Lvk6TitWrMgGDRrU6zePn3DCCdmf//mf52LXDqj+/Nm0dOnS7LDDDuvztwMPJAfiGP2mz/rlUalefw9UwnaA+p//839mpaWl2Y9//OMev37/o48+6p7zF3/xF1lpaWn2xBNPZK+99lr2x3/8x33+SvZx48Zl//iP/5j95Cc/yc4666w+P+7npJNOylavXp2tXr06+y//5b8k8+vG83mc3nrrraypqSm79dZbs8997nNZU1NT1tTUNOA/WiNfx+jtt9/OvvjFL2ZnnXVWtnnz5h7fKwX5Ok7/8i//kt15551ZU1NT9otf/CL753/+5+yrX/1qdtxxx2W//vWv877feyOff99+08aNG5P6rcj5Ok6rVq3Kbr/99qypqSnbsGFDVldXl40dOzb7+te/nvd93hf5PJ/+9E//NDvqqKOyZ555JvvpT3+azZkzJxs1alT23nvv5XWf91a+/8794z/+YxYR2bp16/K2jwdCvo7TL3/5y2zEiBHZH/7hH2Zr167Nfvazn2XXXnttNmTIkGzt2rV53++9lc/z6c4778waGxuzn/3sZ9ldd92VDRs2LPvhD3+Y1/3dFwfqGLW0tGRNTU3Z//7f/7v7N2k3NTVl7e3t3XNSvv4eqITtABURfT4eeOCB7jk7d+7Mbr755mz06NFZUVFR9v/9f/9f9tprr/X4Or/61a+ya665Jhs+fHg2bNiw7Pd///ez5ubmHnPa29uzb37zm9kRRxyRHXHEEdk3v/nNAf8varvk8zhddtllfX6vgX6XLV/H6IEHHtjt90pBvo7Tv/3bv2VnnnlmNnz48KyoqCg75phjsurq6mzz5s352tV9ls+/b78ptbDN13FqbGzMpk6dmpWWlmbFxcXZl770pezmm2/OPvzww3zt6n7J5/m0bdu27Dvf+U42atSo7IgjjsjOPvvs7N///d/zsZv7Jd9/5/74j/84q6yszPVu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9tlnY8qUKTFkyJA+tykqKoqioqJe46WlpX5Ic0CUlJQ4lzggnEscSM4nDhTnEgfKoEF+jVF/cNTzoKamJu6///5YtmxZrF+/PubPnx/Nzc1RXV0dEZ/cbZ09e3b3/Orq6njrrbeipqYm1q9fH8uWLYulS5fGtdde21+7AAAAMGC5Y5sHs2bNivb29li0aFG0tLTEpEmTor6+PiZMmBARES0tLT0+07a8vDzq6+tj/vz5cffdd8fYsWPjjjvuiAsvvLC/dgEAAGDAErZ5ctVVV8VVV13V5589+OCDvcbOOOOM+MlPfrLP36+oqChuvvnmPl+eDHvDucSB4lziQHI+caA4lzhQnEv9qyDz+6gBAABImPfYAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2CZs8eLFUV5eHsXFxVFRURErV6781PnPP/98VFRURHFxcRx77LFxzz335GmlDHR7cy498cQTcc4558QXvvCFKCkpiWnTpsUzzzyTx9UykO3tz6VdXnrppSgsLIxTTjkltwskGXt7LnV1dcXChQtjwoQJUVRUFMcdd1wsW7YsT6tloNvb82n58uVx8sknx2GHHRZjxoyJK664Itrb2/O0WgaqF154IS644IIYO3ZsFBQUxFNPPfWZ27j+zh9hm6i6urqYN29eLFy4MJqammL69OkxY8aMHp+H+5s2btwY5513XkyfPj2amprixhtvjLlz58bjjz+e55Uz0OztufTCCy/EOeecE/X19dHY2BhnnnlmXHDBBdHU1JTnlTPQ7O25tEtHR0fMnj07vva1r+VppQx0+3IuXXTRRfFP//RPsXTp0vjZz34WjzzySJx44ol5XDUD1d6eTy+++GLMnj075syZE6+//no8+uij8corr8SVV16Z55Uz0Hz44Ydx8sknx1133bVH811/51lGkk499dSsurq6x9iJJ56Y3XDDDX3O/1//639lJ554Yo+xb33rW9lpp52WszWShr09l/ry5S9/Obv11lsP9NJIzL6eS7Nmzcr+7M/+LLv55puzk08+OYcrJBV7ey79wz/8Q1ZaWpq1t7fnY3kkZm/Pp7/8y7/Mjj322B5jd9xxRzZu3LicrZH0RET25JNPfuoc19/55Y5tgrZt2xaNjY1RVVXVY7yqqipWrVrV5zarV6/uNf/cc8+NNWvWxMcff5yztTKw7cu59Nt27twZ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atSo3c6rra2N0tLS7sf48eP3e80AAAApELY5NnLkyBg8eHCvu7NbtmzpdRf3t9XV1cWcOXPib/7mb+Lss8/+1LkLFiyIjo6O7semTZv2e+0AAAApELY5NnTo0KioqIiGhoYe4w0NDVFZWbnb7R555JG4/PLL4+GHH47zzz//M79PUVFRlJSU9HgAAAAcCrzHNg9qamri0ksvjSlTpsS0adPivvvui+bm5qiuro6IT+62vv322/HQQw9FxCdRO3v27PjhD38Yp512Wvfd3mHDhkVpaWm/7QcAAMBAJGzzYNasWdHe3h6LFi2KlpaWmDRpUtTX18eECRMiIqKlpaXHZ9ree++9sX379rj66qvj6quv7h6/7LLL4sEHH8z38gEAAAY0n2N7kPI5WgAAkD+uv/uX99gCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2ObJ4sWLo7y8PIqLi6OioiJWrlz5qfOff/75qKioiOLi4jj22GPjnnvuydNKAQAA0iJs86Curi7mzZsXCxcujKamppg+fXrMmDEjmpub+5y/cePGOO+882L69OnR1NQUN954Y8ydOzcef/zxPK8cAABg4CvIsizr70Uc7KZOnRqTJ0+OJUuWdI9NnDgxZs6cGbW1tb3mX3/99bFixYpYv35991h1dXW8+uqrsXr16j36np2dnVFaWhodHR1RUlKy/zsBAADsluvv/lXY3ws42G3bti0aGxvjhhtu6DFeVVUVq1at6nOb1atXR1VVVY+xc889N5YuXRoff/xxDBkypNc2XV1d0dXV1f28o6MjIj75CwYAAOTWrutu9w37h7DNsba2ttixY0eUlZX1GC8rK4vW1tY+t2ltbe1z/vbt26OtrS3GjBnTa5va2tq49dZbe42PHz9+P1YPAADsjfb29igtLe3vZRxyhG2eFBQU9HieZVmvsc+a39f4LgsWLIiampru5++//35MmDAhmpub/cViv3R2dsb48eNj06ZNXlbDfnEucSA5nzhQnEscKB0dHXH00UfH8OHD+3sphyRhm2MjR46MwYMH97o7u2XLll53ZXcZPXp0n/MLCwtjxIgRfW5TVFQURUVFvcZLS0v9kOaAKCkpcS5xQDiXOJCcTxwoziUOlEGD/H7e/uCo59jQoUOjoqIiGhoaeow3NDREZWVln9tMmzat1/xnn302pkyZ0uf7awEAAA5lwjYPampq4v77749ly5bF+vXrY/78+dHc3BzV1dUR8cnLiGfPnt09v7q6Ot56662oqamJ9evXx7Jly2Lp0qVx7bXX9tcuAAAADFheipwHs2bNivb29li0aFG0tLTEpEmTor6+PiZMmBARES0tLT0+07a8vDzq6+tj/vz5cffdd8fYsWPjjjvuiAsvvHCPv2dRUVHcfPPNfb48GfaGc4kDxbnEgeR84kBxLnGgOJf6l8+xBQAAIGleigwAAEDShC0AAABJE7YAAAAkTdgCAACQNGGbsMWLF0d5eXkUFxdHRUVFrFy58lPnP//881FRURHFxcVx7LHHxj333JOnlTLQ7c259MQTT8Q555wTX/jCF6KkpCSmTZsWzzzzTB5Xy0C2tz+XdnnppZeisLAwTjnllNwukGTs7bnU1dUVCxcujAkTJkRRUVEcd9xxsWzZsjytloFub8+n5cuXx8knnxyHHXZYjBkzJq644opob2/P02oZqF544YW44IILYuzYsVFQUBBPPfXUZ27j+jt/hG2i6urqYt68ebFw4cJoamqK6dOnx4wZM3p8bNBv2rhxY5x33nkxffr0aGpqihtvvDHmzp0bjz/+eJ5XzkCzt+fSCy+8EOecc07U19dHY2NjnHnmmXHBBRdEU1NTnlfOQLO359IuHR0dMXv27Pja176Wp5Uy0O3LuXTRRRfFP/3TP8XSpUvjZz/7WTzyyCNx4okn5nHVDFR7ez69+OKLMXv27JgzZ068/vrr8eijj8Yrr7wSV155ZZ5XzkDz4Ycfxsknnxx33XXXHs13/Z1nGUk69dRTs+rq6h5jJ554YnbDDTf0Of9//a//lZ144ok9xr71rW9lp512Ws7WSBr29lzqy5e//OXs1ltvPdBLIzH7ei7NmjUr+7M/+7Ps5ptvzk4++eQcrpBU7O259A//8A9ZaWlp1t7eno/lkZi9PZ/+8i//Mjv22GN7jN1xxx3ZuHHjcrZG0hMR2ZNPPvmpc1x/55c7tgnatm1bNDY2RlVVVY/xqqqqWLVqVZ/brF69utf8c889N9asWRMff/xxztbKwLYv59Jv27lzZ2zdujWGDx+eiyWSiH09lx544IF488034+abb871EknEvpxLK1asiClTpsT3vve9OOqoo+KEE06Ia6+9Nn71q1/lY8kMYPtyPlVWVsbmzZujvr4+siyLd999Nx577LE4//zz87FkDiKuv/OrsL8XwN5ra2uLHTt2RFlZWY/xsrKyaG1t7XOb1tbWPudv37492traYsyYMTlbLwPXvpxLv+373/9+fPjhh3HRRRflYokkYl/OpZ///Odxww03xMqVK6Ow0P+O+MS+nEsbNmyIF198MYqLi+PJJ5+Mtra2uOqqq+K9997zPttD3L6cT5WVlbF8+fKYNWtW/PrXv47t27fH17/+9bjzzjvzsWQOIq6/88sd24QVFBT0eJ5lWa+xz5rf1ziHnr09l3Z55JFH4pZbbom6uroYNWpUrpZHQvb0XNqxY0dccsklceutt8YJJ5yQr+WRkL35ubRz584oKCiI5cuXx6mnnhrnnXde3H777fHggw+6a0tE7N35tG7dupg7d27cdNNN0djYGE8//XRs3Lgxqqur87FUDjKuv/PHP5EnaOTIkTF48OBe/9K4ZcuWXv8qtMvo0aP7nF9YWBgjRozI2VoZ2PblXNqlrq4u5syZE48++micffbZuVwmCdjbc2nr1q2xZs2aaGpqimuuuSYiPomTLMuisLAwnn322TjrrLPysnYGln35uTRmzJg46qijorS0tHts4sSJkWVZbN68OY4//vicrpmBa1/Op9ra2jj99NPjuuuui4iIk046KQ4//PCYPn163Hbbbe6yscdcf+eXO7YJGjp0aFRUVERDQ0OP8YaGhqisrOxzm2nTpvWa/+yzz8aUKVNiyJAhOVsrA9u+nEsRn9ypvfzyy+Phhx/2niMiYu/PpZKSknjttddi7dq13Y/q6ur40pe+FGvXro2pU6fma+kMMPvyc+n000+Pd955Jz744IPusTfeeCMGDRoU48aNy+l6Gdj25Xz66KOPYtCgnpfIgwcPjoj/vNsGe8L1d5710y+tYj/96Ec/yoYMGZItXbo0W7duXTZv3rzs8MMPz37xi19kWZZlN9xwQ3bppZd2z9+wYUN22GGHZfPnz8/WrVuXLV26NBsyZEj22GOP9dcuMEDs7bn08MMPZ4WFhdndd9+dtbS0dD/ef//9/toFBoi9PZd+m9+KzC57ey5t3bo1GzduXPZHf/RH2euvv549//zz2fHHH59deeWV/bULDCB7ez498MADWWFhYbZ48eLszTffzF588cVsypQp2amnntpfu8AAsXXr1qypqSlramrKIiK7/fbbs6ampuytt97Kssz1d38Ttgm7++67swkTJmRDhw7NJk+enD3//PPdf3bZZZdlZ5xxRo/5P/7xj7Pf+Z3fyYYOHZodc8wx2ZIlS/K8YgaqvTmXzjjjjCwiej0uu+yy/C+cAWdvfy79JmHLb9rbc2n9+vXZ2WefnQ0bNiwbN25cVlNTk3300Ud5XjUD1d6eT3fccUf25S9/ORs2bFg2ZsyY7Jvf/Ga2efPmPK+agea555771Gsg19/9qyDLvKYCAACAdHmPLQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACTt/wcLAF7/XlacegAAAABJRU5ErkJggg==", + "text/html": [ + "\n", + "
\n", + "
\n", + " Figure\n", + "
\n", + " \n", + "
\n", + " " + ], + "text/plain": [ + "Canvas(toolbar=Toolbar(toolitems=[('Home', 'Reset original view', 'home', 'home'), ('Back', 'Back to previous …" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'sensor_temperature_rise'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'sensor_temperature_rise'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/OKMSOIL/OKMSOIL_tutorial.ipynb b/VAPs/quicklook/OKMSOIL/OKMSOIL_tutorial.ipynb new file mode 100644 index 00000000..444b458d --- /dev/null +++ b/VAPs/quicklook/OKMSOIL/OKMSOIL_tutorial.ipynb @@ -0,0 +1,838 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# OKMSOIL.C1 Notebook\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/okmsoil) for more information about this vap." + ] + }, + { + "cell_type": "markdown", + "id": "97097763", + "metadata": {}, + "source": [ + "In this notebook, we demonstrate the workflow to explore ARM vap data (using okmsoil as an example.) Value-added products (VAPs) are higher-order data products that have been analyzed and processed to ease scientists’ use of ARM data in atmospheric research and global climate models. \n", + "Here is the main content we will cover." + ] + }, + { + "cell_type": "markdown", + "id": "eddec40f", + "metadata": {}, + "source": [ + "# Table of Content\n", + "## Access the data\n", + "* How to retrieve the data\n", + "* Data path and file name conventions\n", + "* Load data\n", + "## Explore the data\n", + "* NetCDF Data structure\n", + "* Xarray essentials\n", + "* Xarray Variable\n", + "## Plot the data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "import random\n", + "\n", + "import glob\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "2372d5be", + "metadata": {}, + "source": [ + "## Access the data" + ] + }, + { + "cell_type": "markdown", + "id": "5b0d9684", + "metadata": {}, + "source": [ + "### How to retrieve the data\n", + "We assume the path \"/data/archive\" is available where you are running this notebook. You can use `os.path.exists(\"/data/archive\")` to verify if the path exists at your machine.\n", + "\n", + "\n", + "### Data path and file name conventions\n", + "There several common terminologies regarding ARM data, for example, data-stream-name, data-level, site, facility, instrument, etc. (For more details, please see [ARM Data File Standards Version 1.2](https://www.google.com/search?q=arm+datastream+facility+cite+definition&rlz=1C1GCEJ_enUS1029US1029&ei=hb41ZICrBPukqtsPvMODIA&ved=0ahUKEwjAgeiS0aL-AhV7kmoFHbzhAAQQ4dUDCBA&uact=5&oq=arm+datastream+facility+cite+definition&gs_lcp=Cgxnd3Mtd2l6LXNlcnAQAzIFCAAQogQyBQgAEKIEMgUIABCiBDIFCAAQogQyBQgAEKIEOgoIABBHENYEELADOgoIIRCgARDDBBAKSgQIQRgAUJ4DWIoOYO0PaAFwAXgAgAGMAYgBuQmSAQMyLjmYAQCgAQHIAQjAAQE&sclient=gws-wiz-serp).) \n", + "\n", + "For example, this notebook is called `okmsoil.c1`, where `okmsoil` is the \"datastream name\", and the `{process.ds_class_level}}` is the \"data level\".\n", + "\n", + "This datastream also contains site `sgp` and facility `X1`. (Note: individual datastream might have multiple site-facility pairs.)\n", + "In such a case, the data of this data-stream is stored at `/data/archive/sgp/sgpokmsoilX1.c1`, which is in the format of `//.`. We can use the following method to assign the data-stream directory `datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )`\n", + "\n", + "The data files under datastream_dir also follows naming conventions. But once reach the datastream_dir level, the most import file naming convention to differentiate the files is \"yyyyMMdd.hhmmss\", which comes handy to filter out files based on datetime. For example, we can use `glob.glob(f'{datastream_dir}/*.200709*.*')` to filter files in 2007 September.\n", + "\n", + "(Note: refer to the https://adc.arm.gov/discovery/#/ and https://adc.arm.gov/solr8/metadata/select API to explore ARM datastream and assoicated available sites and facilities\n", + "\n", + "Please see the following examples in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d7e9eb85", + "metadata": {}, + "outputs": [], + "source": [ + "# Verify if DATA_DIR path exists\n", + "DATA_DIR = \"/data/archive\"\n", + "os.path.exists(DATA_DIR)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "586993fd", + "metadata": {}, + "outputs": [], + "source": [ + "# Speicify datastream_dir following the path conventions and check its existence\n", + "DATASTREAM_NAME = \"okmsoil\"\n", + "DATA_LEVEL = \"c1\"\n", + "site = \"sgp\"\n", + "facility = \"X1\"\n", + "datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )\n", + "print(datastream_dir)\n", + "print(os.path.exists(datastream_dir))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0742f7c1", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: list 5 (random) files under datastream_dir\n", + "files = os.listdir(datastream_dir)\n", + "files[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39b98a36", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: get most recent file\n", + "list_of_files = glob.glob(f\"{datastream_dir}/*\") # * means all if need specific format then *.csv\n", + "latest_file = max(list_of_files, key=os.path.getctime)\n", + "latest_file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "902d514e", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus sort datastream files based on datetime\n", + "files = os.listdir(datastream_dir)\n", + "file_sorted = files.copy()\n", + "file_sorted.sort() \n", + "print(file_sorted[:5])\n", + "\n", + "# to reverse\n", + "file_sorted_reverse = files.copy() \n", + "file_sorted_reverse.sort(reverse=True)\n", + "print(file_sorted_reverse[:5])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec5923b2", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: pattern matching\n", + "# filter the 200709** files under datastream_dir\n", + "files_filter = glob.glob(f'{datastream_dir}/*.200709*.*')\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c4a4aa18", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: use armnotebook_utils.file_filter (TODO) to filter files based on datastream info \n" + ] + }, + { + "cell_type": "markdown", + "id": "cfeb9efc", + "metadata": {}, + "source": [ + "### Load data\n", + "The arm data is stored in [NetCDF](https://en.wikipedia.org/wiki/NetCDF#:~:text=%22NetCDF%20(network%20Common%20Data%20Form,format%20for%20representing%20scientific%20data.) format. We can use xarray's `open_dataset` method to load single file, or `open_mfdataset` to open multiple files. (Note: the latter will still return a single xarray dataset object by combining multiple files.)\n", + "\n", + "Note: open_dataset keeps the file handle open and lazy loads its contents. All parameters are passed directly to open_dataset. It is a preferable method over load_dataset for memory-effiency. (more details, please see [xarray.open_dataset](https://docs.xarray.dev/en/stable/generated/xarray.open_dataset.html))\n", + "\n", + "See the following example in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a440a329", + "metadata": {}, + "outputs": [], + "source": [ + "# open single file\n", + "full_path = latest_file\n", + "print(full_path)\n", + "ds_single = xr.open_dataset(full_path)\n", + "ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0143a3d", + "metadata": {}, + "outputs": [], + "source": [ + "print(ds_single)\n", + "print(type(ds_single))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c0f8939", + "metadata": {}, + "outputs": [], + "source": [ + "# open multiple files\n", + "n_files = 3\n", + "full_paths = [os.path.join(datastream_dir, f_path) for f_path in file_sorted_reverse[:n_files]]\n", + "print(full_paths)\n", + "try: # Note: sometimes multiple files cannot be merged, so we used try except here.\n", + " ds_mutiple = xr.open_mfdataset(full_paths)\n", + " # ds_mutiple\n", + " print(type(ds_mutiple))\n", + " print(ds_mutiple)\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "3fa59943", + "metadata": {}, + "source": [ + "## Explore the data" + ] + }, + { + "cell_type": "markdown", + "id": "571f69b9", + "metadata": {}, + "source": [ + "### NetCDF Data structure \n", + "\n", + "\n", + "(If you are confident with NetCDF basics and xarray essentials, feel free to skip this session.)\n", + "Before we dive into data exploration, there are some eseential concpets we should be familiar with (shown below): \n", + "* Dataset\n", + "* Data array\n", + "* Variable\n", + "* Dimenssion\n", + "* Coordinate\n", + "* Data Type\n", + "* Meta Data (Attributes)\n", + "\n", + "We will not go into details about NetCDF basics and here are some references you might find helpful\n", + "* [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#)\n", + "* [Network Common Data Form (NetCDF)](https://www.unidata.ucar.edu/software/netcdf/) and [A Brief History of (netCDF) Time](https://www.unidata.ucar.edu/software/netcdf/time/recs.html)\n", + "\n", + "\n", + "\n", + "### Xarray essentials\n", + "Earlier we introduced the xarray pacakge and used `open_dataset` and `open_mfdataset` to retrieve NetCDF data file as an xarray Dataset object. Recall that you can review the data in a notebook by using `print(ds)` or `ds`. \n", + "\n", + "Using xarray to retrieve the aforementioned NetCDF basics is straightforwared. In general,\n", + "* Dataset: `ds`\n", + "* Data array: `ds.variables`\n", + "* Variable: `ds.ds.variables`\n", + "* Dimenssion: `ds.dims`\n", + "* Coordinate: `ds.coords`\n", + "* Data Type: `type`\n", + "* Meta Data (Attributes)\n", + "\n", + "Also, here are some references if you are new to xarray\n", + "* [Xarray in 45 minutes](https://tutorial.xarray.dev/overview/xarray-in-45-min.html)\n", + "* [Handling NetCDF Files using XArray for Absolute Beginners](https://towardsdatascience.com/handling-netcdf-files-using-xarray-for-absolute-beginners-111a8ab4463f)\n", + "\n", + "Try the following commands in action\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b3eac323", + "metadata": {}, + "outputs": [], + "source": [ + "ds = ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77ecf85d", + "metadata": {}, + "outputs": [], + "source": [ + "# Dataset \n", + "ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25e7de09", + "metadata": {}, + "outputs": [], + "source": [ + "# Data array, Variable\n", + "# Note: the info can be overwhelming. Do not attempt to grasp everything at the first glance.\n", + "ds.variables" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c41b67e", + "metadata": {}, + "outputs": [], + "source": [ + "# dimenssions\n", + "ds.dims" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "156f1dfc", + "metadata": {}, + "outputs": [], + "source": [ + "# coordinates\n", + "ds.coords" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "277d6064", + "metadata": {}, + "outputs": [], + "source": [ + "# Meta Data (Attributes)\n", + "ds.attrs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d334681f", + "metadata": {}, + "outputs": [], + "source": [ + "# type\n", + "print(type(ds))\n", + "print(type(ds.variables))\n", + "print(type(ds.dims))\n", + "print(type(ds.coords))\n", + "print(type(ds.attrs))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "643399d6", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: retrieve variable names only\n", + "list(ds.variables)" + ] + }, + { + "cell_type": "markdown", + "id": "1cf36878", + "metadata": {}, + "source": [ + "#### Discussion: variable vs. coordinates vs. dimenssions. \n", + "From [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#):\n", + "\n", + "* Dimession: A dimension may be used to represent a real physical dimension, for example, time, latitude, longitude, or height. A dimension might also be used to index other quantities, for example station or model-run-number.\n", + "A netCDF dimension has both a name and a length. A dimension length is an arbitrary positive integer, except that one dimension in a netCDF dataset can have the length UNLIMITED.\n", + "\n", + "* Variables: Variables are used to store the bulk of the data in a netCDF dataset. A variable represents an array of values of the same type. A scalar value is treated as a 0-dimensional array. A variable has a name, a data type, and a shape described by its list of dimensions specified when the variable is created. A variable may also have associated attributes, which may be added, deleted or changed after the variable is created.\n", + "\n", + "* Coordinate (Variables): It is legal for a variable to have the same name as a dimension. Such variables have no special meaning to the netCDF library. However there is a convention that such variables should be treated in a special way by software using this library.\n", + "A variable with the same name as a dimension is called a coordinate variable. It typically defines a physical coordinate corresponding to that dimension.\n", + "\n", + "\n", + "Tips: By definition, coordinates and dimenssions are also variable. For example ds.time is a coordinate but it is also a special variable. But in practice, when we talk about variable, it implies regular/non-coordinate variable. We will use this convention for the remaining notebook. " + ] + }, + { + "cell_type": "markdown", + "id": "d47120f9", + "metadata": {}, + "source": [ + "### Xarray Variable\n", + "\n", + "\n", + "We can use `ds.variables` to access varialbes of a dataset. But many times viewing print-out the whole datasets can be overwhemling. Instead, we would work on the individual variable (also called Xarray data array.) using the `ds[var_name]` syntax, where `var_name` is the name of the variable. See the following examples in action.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5a57e136", + "metadata": {}, + "outputs": [], + "source": [ + "print(type(ds[\"time\"]))\n", + "ds[\"time\"]" + ] + }, + { + "cell_type": "markdown", + "id": "51ec643e", + "metadata": {}, + "source": [ + "#### Variable properties\n", + "For individual varible, the following properties are mostly used with the assocaited xarray method to retrieve them. (assuming var = ds[var_name])\n", + "* name: `var.name`\n", + "* data content: `var.data`\n", + "* attributes: `var.attrs`\n", + "* dimenstions: `var.dims`\n", + "* data type: `var.data.dtype`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a632a525", + "metadata": {}, + "outputs": [], + "source": [ + "var_name = \"time\"\n", + "var = ds[var_name]\n", + "\n", + "print(\"var.name: \\n\", var.name, \"\\n\")\n", + "print(\"var.data: \\n\", var.data, \"\\n\")\n", + "print(\"var.attrs: \\n\", var.attrs, \"\\n\")\n", + "print(\"var.dims: \\n\", var.dims, \"\\n\")\n", + "print(\"var.data.dtype: \\n\", var.data.dtype, \"\\n\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b256b07f", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: indivdual variable info table (collect and display variable info as a tabular format)\n", + "df_info = pd.DataFrame()\n", + "df_info[\"var_name\"] = list(ds.variables)\n", + "df_info[\"dims\"] = df_info.var_name.apply(lambda x: ds[x].dims)\n", + "df_info[\"is_dim\"] = df_info.var_name.apply(lambda x: x in ds.dims)\n", + "df_info[\"n_dim\"] = df_info.var_name.apply(lambda x: len(ds[x].dims))\n", + "df_info[\"attrs\"] = df_info.var_name.apply(lambda x: ds[x].attrs)\n", + "df_info[\"dtype\"] = df_info.var_name.apply(lambda x: ds[x].data.dtype)\n", + "df_info" + ] + }, + { + "cell_type": "markdown", + "id": "e6a36b96", + "metadata": {}, + "source": [ + "### Data cleaning/Preprocessing (skipped)\n", + "Data cleaning and preprocessing is an important stage in the the data analysis pipeline. However it will be out of the scope of this notebook. For more information about data cleaning and preprocessing basics, here are some references. \n", + "* [Xarray Fundamentals](https://earth-env-data-science.github.io/lectures/xarray/xarray.html)\n", + "* [Xarray Tutorial — Pangeo Gallery documentation](http://gallery.pangeo.io/repos/pangeo-data/pangeo-tutorial-gallery/xarray.html)\n", + "* [Pythonic Data Cleaning With pandas and NumPy](https://realpython.com/python-data-cleaning-numpy-pandas/)\n", + "* [Pandas - Cleaning Data](https://www.w3schools.com/python/pandas/pandas_cleaning.asp)" + ] + }, + { + "cell_type": "markdown", + "id": "ae6e1a69", + "metadata": {}, + "source": [ + "### Plotting\n", + "Data visualization (or plotting) is another important data analysis topic and deserves its own discussion. Here in this notebook we will only demonstrate basic tool for simple data visualization tasks.\n", + "\n", + "Here are some reference you might find useful:\n", + "* xarray plotting: https://docs.xarray.dev/en/stable/user-guide/plotting.html\n", + "* Atmospheric Community Toolkit (ACT): https://arm-doe.github.io/ACT/index.html\n", + "\n", + "Note: this notebook is auto-generated using a template. It uses a general idea to select variable(s) to plot and is not customized to each indivdual notebook. Feel free to change the variables in intrrests, especially certain figure is failed to plot." + ] + }, + { + "cell_type": "markdown", + "id": "7e417fb2", + "metadata": {}, + "source": [ + "#### 1-dimenssional basic time series plot\n", + "\n", + "For the following plot we would like to find variables such that\n", + "* it has one and only one dimession\n", + "* \"time\" is its coordinate variable\n", + "* it is not a dimenssion itself,\n", + "* it is not a special variable with substrings within [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "\n", + "Please see the following example in action. (For more details about pandas filtering, please see the following references.)\n", + "* [pandas: multiple conditions while indexing data frame](https://stackoverflow.com/questions/22591174/pandas-multiple-conditions-while-indexing-data-frame-unexpected-behavior)\n", + "* [pandas.DataFrame.apply](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.apply.html)\n", + "* [How to test if a string contains one of the substrings in a list...](https://stackoverflow.com/questions/26577516/how-to-test-if-a-string-contains-one-of-the-substrings-in-a-list-in-pandas" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30edac2d", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==1, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter = df_info[(df_info.n_dim == 1) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4777a659", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_1d = df_filter.var_name.values[0]\n", + " var_1d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb5ae985", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " ds[var_1d].plot()\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "9e41066e", + "metadata": {}, + "source": [ + "#### 2-dimenssional basic plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09bd4adc", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==2, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_2 = df_info[(df_info.n_dim == 2) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2cdea1a9", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_2d = df_filter_2.var_name.values[0]\n", + " var_2d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5f48879", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " print(ds[var_2d].dims)\n", + " # ds[var_2d].plot()\n", + "\n", + " # conventionally, use \"time\" as x-axis\n", + " ds[var_2d].plot(x=\"time\")\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "7554b2a6", + "metadata": {}, + "source": [ + "#### qc-plotting (optional)\n", + "\n", + "Note: act qc plotting has more strict requirement, one of them is the associated qc variable need to have \"flag_masks\" attributes. Which is added by using ds = act.io.armfiles.read_netcdf(files_list), then ds.clean.cleanup()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71c2096f", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " ds_act = act.io.armfiles.read_netcdf(full_path)\n", + " print(type(ds_act))\n", + " ds_act.clean.cleanup()\n", + "\n", + " # or \n", + " # ds.clean.cleanup()\n", + "except Exception as e:\n", + " print(\"ERROR\", e)\n", + " ds_act = ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "844f8505", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter valid variables for ACT qc plotting\n", + "condition = (\"qc_\" + df_info.var_name).apply(lambda x: ds_act[x].attrs.get(\"flag_masks\") is not None \n", + " if x in list(ds.variables) else False)\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_3 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings))) &\n", + " condition\n", + " ]\n", + "df_filter_3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd38cc6", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "try:\n", + " qc_variable = df_filter_3.var_name.values[0]\n", + " print(qc_variable)\n", + "except Exception as e:\n", + " print(e)\n", + " \n", + "try:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + " qc_ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "d76e4d27", + "metadata": {}, + "source": [ + "#### bonus: choose variables to plot from a dropdown menu " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb733804", + "metadata": {}, + "outputs": [], + "source": [ + "# Valid variables filtering\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_4 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82d5c20d", + "metadata": {}, + "outputs": [], + "source": [ + "# example 1: using xarray plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2fc9aaa4", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# fig, ax = plt.subplots(figsize=(10, 4))\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "# fig.clear() # Remove old lines from plot and plot new one\n", + "# if len(ds[var].dims)==2:\n", + "# ds[var].plot(x=\"time\", add_colorbar=False)\n", + "# else:\n", + "# ds[var].plot()\n", + "# plt.grid()\n", + "# plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fabdd802", + "metadata": {}, + "outputs": [], + "source": [ + "# example 2: using act plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "962b4186", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "\n", + "\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "\n", + "# i_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + "# i_display.add_subplots((1,), figsize=(10, 4))\n", + "# ax = i_display.plot(var, subplot_index=(0,), set_title=f\"{var} ({ds_act[var].attrs['long_name']})\",)\n", + "\n", + "# ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "# ax.grid()\n", + "# plt.show()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/OKMSOIL/okmsoil.c1.ipynb b/VAPs/quicklook/OKMSOIL/okmsoil.c1.ipynb new file mode 100644 index 00000000..57911f93 --- /dev/null +++ b/VAPs/quicklook/OKMSOIL/okmsoil.c1.ipynb @@ -0,0 +1,339 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# OKMSOIL.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/okmsoil) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'okmsoil'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2020-10-22', 'facility': 'X1', 'site': 'sgp', 'start_date': '1998-01-01'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'X1' )\n", + "\n", + "date_start = '2020-10-20'\n", + "date_end = '2020-10-22'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['sensor_temperature_rise', 'matric_potential', 'volumetric_water_content']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'sensor_temperature_rise'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'sensor_temperature_rise'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/OZONE-AIR/OZONE-AIR_tutorial.ipynb b/VAPs/quicklook/OZONE-AIR/OZONE-AIR_tutorial.ipynb new file mode 100644 index 00000000..34aa8f95 --- /dev/null +++ b/VAPs/quicklook/OZONE-AIR/OZONE-AIR_tutorial.ipynb @@ -0,0 +1,838 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# AAFO3.C1 Notebook\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/ozone-air) for more information about this vap." + ] + }, + { + "cell_type": "markdown", + "id": "97097763", + "metadata": {}, + "source": [ + "In this notebook, we demonstrate the workflow to explore ARM vap data (using aafo3 as an example.) Value-added products (VAPs) are higher-order data products that have been analyzed and processed to ease scientists’ use of ARM data in atmospheric research and global climate models. \n", + "Here is the main content we will cover." + ] + }, + { + "cell_type": "markdown", + "id": "eddec40f", + "metadata": {}, + "source": [ + "# Table of Content\n", + "## Access the data\n", + "* How to retrieve the data\n", + "* Data path and file name conventions\n", + "* Load data\n", + "## Explore the data\n", + "* NetCDF Data structure\n", + "* Xarray essentials\n", + "* Xarray Variable\n", + "## Plot the data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "import random\n", + "\n", + "import glob\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "2372d5be", + "metadata": {}, + "source": [ + "## Access the data" + ] + }, + { + "cell_type": "markdown", + "id": "5b0d9684", + "metadata": {}, + "source": [ + "### How to retrieve the data\n", + "We assume the path \"/data/archive\" is available where you are running this notebook. You can use `os.path.exists(\"/data/archive\")` to verify if the path exists at your machine.\n", + "\n", + "\n", + "### Data path and file name conventions\n", + "There several common terminologies regarding ARM data, for example, data-stream-name, data-level, site, facility, instrument, etc. (For more details, please see [ARM Data File Standards Version 1.2](https://www.google.com/search?q=arm+datastream+facility+cite+definition&rlz=1C1GCEJ_enUS1029US1029&ei=hb41ZICrBPukqtsPvMODIA&ved=0ahUKEwjAgeiS0aL-AhV7kmoFHbzhAAQQ4dUDCBA&uact=5&oq=arm+datastream+facility+cite+definition&gs_lcp=Cgxnd3Mtd2l6LXNlcnAQAzIFCAAQogQyBQgAEKIEMgUIABCiBDIFCAAQogQyBQgAEKIEOgoIABBHENYEELADOgoIIRCgARDDBBAKSgQIQRgAUJ4DWIoOYO0PaAFwAXgAgAGMAYgBuQmSAQMyLjmYAQCgAQHIAQjAAQE&sclient=gws-wiz-serp).) \n", + "\n", + "For example, this notebook is called `aafo3.c1`, where `aafo3` is the \"datastream name\", and the `{process.ds_class_level}}` is the \"data level\".\n", + "\n", + "This datastream also contains site `cor` and facility `F1`. (Note: individual datastream might have multiple site-facility pairs.)\n", + "In such a case, the data of this data-stream is stored at `/data/archive/cor/coraafo3F1.c1`, which is in the format of `//.`. We can use the following method to assign the data-stream directory `datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )`\n", + "\n", + "The data files under datastream_dir also follows naming conventions. But once reach the datastream_dir level, the most import file naming convention to differentiate the files is \"yyyyMMdd.hhmmss\", which comes handy to filter out files based on datetime. For example, we can use `glob.glob(f'{datastream_dir}/*.200709*.*')` to filter files in 2007 September.\n", + "\n", + "(Note: refer to the https://adc.arm.gov/discovery/#/ and https://adc.arm.gov/solr8/metadata/select API to explore ARM datastream and assoicated available sites and facilities\n", + "\n", + "Please see the following examples in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d7e9eb85", + "metadata": {}, + "outputs": [], + "source": [ + "# Verify if DATA_DIR path exists\n", + "DATA_DIR = \"/data/archive\"\n", + "os.path.exists(DATA_DIR)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "586993fd", + "metadata": {}, + "outputs": [], + "source": [ + "# Speicify datastream_dir following the path conventions and check its existence\n", + "DATASTREAM_NAME = \"aafo3\"\n", + "DATA_LEVEL = \"c1\"\n", + "site = \"cor\"\n", + "facility = \"F1\"\n", + "datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )\n", + "print(datastream_dir)\n", + "print(os.path.exists(datastream_dir))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0742f7c1", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: list 5 (random) files under datastream_dir\n", + "files = os.listdir(datastream_dir)\n", + "files[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39b98a36", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: get most recent file\n", + "list_of_files = glob.glob(f\"{datastream_dir}/*\") # * means all if need specific format then *.csv\n", + "latest_file = max(list_of_files, key=os.path.getctime)\n", + "latest_file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "902d514e", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus sort datastream files based on datetime\n", + "files = os.listdir(datastream_dir)\n", + "file_sorted = files.copy()\n", + "file_sorted.sort() \n", + "print(file_sorted[:5])\n", + "\n", + "# to reverse\n", + "file_sorted_reverse = files.copy() \n", + "file_sorted_reverse.sort(reverse=True)\n", + "print(file_sorted_reverse[:5])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec5923b2", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: pattern matching\n", + "# filter the 200709** files under datastream_dir\n", + "files_filter = glob.glob(f'{datastream_dir}/*.200709*.*')\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c4a4aa18", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: use armnotebook_utils.file_filter (TODO) to filter files based on datastream info \n" + ] + }, + { + "cell_type": "markdown", + "id": "cfeb9efc", + "metadata": {}, + "source": [ + "### Load data\n", + "The arm data is stored in [NetCDF](https://en.wikipedia.org/wiki/NetCDF#:~:text=%22NetCDF%20(network%20Common%20Data%20Form,format%20for%20representing%20scientific%20data.) format. We can use xarray's `open_dataset` method to load single file, or `open_mfdataset` to open multiple files. (Note: the latter will still return a single xarray dataset object by combining multiple files.)\n", + "\n", + "Note: open_dataset keeps the file handle open and lazy loads its contents. All parameters are passed directly to open_dataset. It is a preferable method over load_dataset for memory-effiency. (more details, please see [xarray.open_dataset](https://docs.xarray.dev/en/stable/generated/xarray.open_dataset.html))\n", + "\n", + "See the following example in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a440a329", + "metadata": {}, + "outputs": [], + "source": [ + "# open single file\n", + "full_path = latest_file\n", + "print(full_path)\n", + "ds_single = xr.open_dataset(full_path)\n", + "ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0143a3d", + "metadata": {}, + "outputs": [], + "source": [ + "print(ds_single)\n", + "print(type(ds_single))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c0f8939", + "metadata": {}, + "outputs": [], + "source": [ + "# open multiple files\n", + "n_files = 3\n", + "full_paths = [os.path.join(datastream_dir, f_path) for f_path in file_sorted_reverse[:n_files]]\n", + "print(full_paths)\n", + "try: # Note: sometimes multiple files cannot be merged, so we used try except here.\n", + " ds_mutiple = xr.open_mfdataset(full_paths)\n", + " # ds_mutiple\n", + " print(type(ds_mutiple))\n", + " print(ds_mutiple)\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "3fa59943", + "metadata": {}, + "source": [ + "## Explore the data" + ] + }, + { + "cell_type": "markdown", + "id": "571f69b9", + "metadata": {}, + "source": [ + "### NetCDF Data structure \n", + "\n", + "\n", + "(If you are confident with NetCDF basics and xarray essentials, feel free to skip this session.)\n", + "Before we dive into data exploration, there are some eseential concpets we should be familiar with (shown below): \n", + "* Dataset\n", + "* Data array\n", + "* Variable\n", + "* Dimenssion\n", + "* Coordinate\n", + "* Data Type\n", + "* Meta Data (Attributes)\n", + "\n", + "We will not go into details about NetCDF basics and here are some references you might find helpful\n", + "* [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#)\n", + "* [Network Common Data Form (NetCDF)](https://www.unidata.ucar.edu/software/netcdf/) and [A Brief History of (netCDF) Time](https://www.unidata.ucar.edu/software/netcdf/time/recs.html)\n", + "\n", + "\n", + "\n", + "### Xarray essentials\n", + "Earlier we introduced the xarray pacakge and used `open_dataset` and `open_mfdataset` to retrieve NetCDF data file as an xarray Dataset object. Recall that you can review the data in a notebook by using `print(ds)` or `ds`. \n", + "\n", + "Using xarray to retrieve the aforementioned NetCDF basics is straightforwared. In general,\n", + "* Dataset: `ds`\n", + "* Data array: `ds.variables`\n", + "* Variable: `ds.ds.variables`\n", + "* Dimenssion: `ds.dims`\n", + "* Coordinate: `ds.coords`\n", + "* Data Type: `type`\n", + "* Meta Data (Attributes)\n", + "\n", + "Also, here are some references if you are new to xarray\n", + "* [Xarray in 45 minutes](https://tutorial.xarray.dev/overview/xarray-in-45-min.html)\n", + "* [Handling NetCDF Files using XArray for Absolute Beginners](https://towardsdatascience.com/handling-netcdf-files-using-xarray-for-absolute-beginners-111a8ab4463f)\n", + "\n", + "Try the following commands in action\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b3eac323", + "metadata": {}, + "outputs": [], + "source": [ + "ds = ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77ecf85d", + "metadata": {}, + "outputs": [], + "source": [ + "# Dataset \n", + "ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25e7de09", + "metadata": {}, + "outputs": [], + "source": [ + "# Data array, Variable\n", + "# Note: the info can be overwhelming. Do not attempt to grasp everything at the first glance.\n", + "ds.variables" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c41b67e", + "metadata": {}, + "outputs": [], + "source": [ + "# dimenssions\n", + "ds.dims" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "156f1dfc", + "metadata": {}, + "outputs": [], + "source": [ + "# coordinates\n", + "ds.coords" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "277d6064", + "metadata": {}, + "outputs": [], + "source": [ + "# Meta Data (Attributes)\n", + "ds.attrs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d334681f", + "metadata": {}, + "outputs": [], + "source": [ + "# type\n", + "print(type(ds))\n", + "print(type(ds.variables))\n", + "print(type(ds.dims))\n", + "print(type(ds.coords))\n", + "print(type(ds.attrs))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "643399d6", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: retrieve variable names only\n", + "list(ds.variables)" + ] + }, + { + "cell_type": "markdown", + "id": "1cf36878", + "metadata": {}, + "source": [ + "#### Discussion: variable vs. coordinates vs. dimenssions. \n", + "From [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#):\n", + "\n", + "* Dimession: A dimension may be used to represent a real physical dimension, for example, time, latitude, longitude, or height. A dimension might also be used to index other quantities, for example station or model-run-number.\n", + "A netCDF dimension has both a name and a length. A dimension length is an arbitrary positive integer, except that one dimension in a netCDF dataset can have the length UNLIMITED.\n", + "\n", + "* Variables: Variables are used to store the bulk of the data in a netCDF dataset. A variable represents an array of values of the same type. A scalar value is treated as a 0-dimensional array. A variable has a name, a data type, and a shape described by its list of dimensions specified when the variable is created. A variable may also have associated attributes, which may be added, deleted or changed after the variable is created.\n", + "\n", + "* Coordinate (Variables): It is legal for a variable to have the same name as a dimension. Such variables have no special meaning to the netCDF library. However there is a convention that such variables should be treated in a special way by software using this library.\n", + "A variable with the same name as a dimension is called a coordinate variable. It typically defines a physical coordinate corresponding to that dimension.\n", + "\n", + "\n", + "Tips: By definition, coordinates and dimenssions are also variable. For example ds.time is a coordinate but it is also a special variable. But in practice, when we talk about variable, it implies regular/non-coordinate variable. We will use this convention for the remaining notebook. " + ] + }, + { + "cell_type": "markdown", + "id": "d47120f9", + "metadata": {}, + "source": [ + "### Xarray Variable\n", + "\n", + "\n", + "We can use `ds.variables` to access varialbes of a dataset. But many times viewing print-out the whole datasets can be overwhemling. Instead, we would work on the individual variable (also called Xarray data array.) using the `ds[var_name]` syntax, where `var_name` is the name of the variable. See the following examples in action.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5a57e136", + "metadata": {}, + "outputs": [], + "source": [ + "print(type(ds[\"time\"]))\n", + "ds[\"time\"]" + ] + }, + { + "cell_type": "markdown", + "id": "51ec643e", + "metadata": {}, + "source": [ + "#### Variable properties\n", + "For individual varible, the following properties are mostly used with the assocaited xarray method to retrieve them. (assuming var = ds[var_name])\n", + "* name: `var.name`\n", + "* data content: `var.data`\n", + "* attributes: `var.attrs`\n", + "* dimenstions: `var.dims`\n", + "* data type: `var.data.dtype`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a632a525", + "metadata": {}, + "outputs": [], + "source": [ + "var_name = \"time\"\n", + "var = ds[var_name]\n", + "\n", + "print(\"var.name: \\n\", var.name, \"\\n\")\n", + "print(\"var.data: \\n\", var.data, \"\\n\")\n", + "print(\"var.attrs: \\n\", var.attrs, \"\\n\")\n", + "print(\"var.dims: \\n\", var.dims, \"\\n\")\n", + "print(\"var.data.dtype: \\n\", var.data.dtype, \"\\n\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b256b07f", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: indivdual variable info table (collect and display variable info as a tabular format)\n", + "df_info = pd.DataFrame()\n", + "df_info[\"var_name\"] = list(ds.variables)\n", + "df_info[\"dims\"] = df_info.var_name.apply(lambda x: ds[x].dims)\n", + "df_info[\"is_dim\"] = df_info.var_name.apply(lambda x: x in ds.dims)\n", + "df_info[\"n_dim\"] = df_info.var_name.apply(lambda x: len(ds[x].dims))\n", + "df_info[\"attrs\"] = df_info.var_name.apply(lambda x: ds[x].attrs)\n", + "df_info[\"dtype\"] = df_info.var_name.apply(lambda x: ds[x].data.dtype)\n", + "df_info" + ] + }, + { + "cell_type": "markdown", + "id": "e6a36b96", + "metadata": {}, + "source": [ + "### Data cleaning/Preprocessing (skipped)\n", + "Data cleaning and preprocessing is an important stage in the the data analysis pipeline. However it will be out of the scope of this notebook. For more information about data cleaning and preprocessing basics, here are some references. \n", + "* [Xarray Fundamentals](https://earth-env-data-science.github.io/lectures/xarray/xarray.html)\n", + "* [Xarray Tutorial — Pangeo Gallery documentation](http://gallery.pangeo.io/repos/pangeo-data/pangeo-tutorial-gallery/xarray.html)\n", + "* [Pythonic Data Cleaning With pandas and NumPy](https://realpython.com/python-data-cleaning-numpy-pandas/)\n", + "* [Pandas - Cleaning Data](https://www.w3schools.com/python/pandas/pandas_cleaning.asp)" + ] + }, + { + "cell_type": "markdown", + "id": "ae6e1a69", + "metadata": {}, + "source": [ + "### Plotting\n", + "Data visualization (or plotting) is another important data analysis topic and deserves its own discussion. Here in this notebook we will only demonstrate basic tool for simple data visualization tasks.\n", + "\n", + "Here are some reference you might find useful:\n", + "* xarray plotting: https://docs.xarray.dev/en/stable/user-guide/plotting.html\n", + "* Atmospheric Community Toolkit (ACT): https://arm-doe.github.io/ACT/index.html\n", + "\n", + "Note: this notebook is auto-generated using a template. It uses a general idea to select variable(s) to plot and is not customized to each indivdual notebook. Feel free to change the variables in intrrests, especially certain figure is failed to plot." + ] + }, + { + "cell_type": "markdown", + "id": "7e417fb2", + "metadata": {}, + "source": [ + "#### 1-dimenssional basic time series plot\n", + "\n", + "For the following plot we would like to find variables such that\n", + "* it has one and only one dimession\n", + "* \"time\" is its coordinate variable\n", + "* it is not a dimenssion itself,\n", + "* it is not a special variable with substrings within [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "\n", + "Please see the following example in action. (For more details about pandas filtering, please see the following references.)\n", + "* [pandas: multiple conditions while indexing data frame](https://stackoverflow.com/questions/22591174/pandas-multiple-conditions-while-indexing-data-frame-unexpected-behavior)\n", + "* [pandas.DataFrame.apply](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.apply.html)\n", + "* [How to test if a string contains one of the substrings in a list...](https://stackoverflow.com/questions/26577516/how-to-test-if-a-string-contains-one-of-the-substrings-in-a-list-in-pandas" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30edac2d", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==1, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter = df_info[(df_info.n_dim == 1) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4777a659", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_1d = df_filter.var_name.values[0]\n", + " var_1d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb5ae985", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " ds[var_1d].plot()\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "9e41066e", + "metadata": {}, + "source": [ + "#### 2-dimenssional basic plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09bd4adc", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==2, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_2 = df_info[(df_info.n_dim == 2) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2cdea1a9", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_2d = df_filter_2.var_name.values[0]\n", + " var_2d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5f48879", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " print(ds[var_2d].dims)\n", + " # ds[var_2d].plot()\n", + "\n", + " # conventionally, use \"time\" as x-axis\n", + " ds[var_2d].plot(x=\"time\")\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "7554b2a6", + "metadata": {}, + "source": [ + "#### qc-plotting (optional)\n", + "\n", + "Note: act qc plotting has more strict requirement, one of them is the associated qc variable need to have \"flag_masks\" attributes. Which is added by using ds = act.io.armfiles.read_netcdf(files_list), then ds.clean.cleanup()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71c2096f", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " ds_act = act.io.armfiles.read_netcdf(full_path)\n", + " print(type(ds_act))\n", + " ds_act.clean.cleanup()\n", + "\n", + " # or \n", + " # ds.clean.cleanup()\n", + "except Exception as e:\n", + " print(\"ERROR\", e)\n", + " ds_act = ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "844f8505", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter valid variables for ACT qc plotting\n", + "condition = (\"qc_\" + df_info.var_name).apply(lambda x: ds_act[x].attrs.get(\"flag_masks\") is not None \n", + " if x in list(ds.variables) else False)\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_3 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings))) &\n", + " condition\n", + " ]\n", + "df_filter_3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd38cc6", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "try:\n", + " qc_variable = df_filter_3.var_name.values[0]\n", + " print(qc_variable)\n", + "except Exception as e:\n", + " print(e)\n", + " \n", + "try:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + " qc_ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "d76e4d27", + "metadata": {}, + "source": [ + "#### bonus: choose variables to plot from a dropdown menu " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb733804", + "metadata": {}, + "outputs": [], + "source": [ + "# Valid variables filtering\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_4 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82d5c20d", + "metadata": {}, + "outputs": [], + "source": [ + "# example 1: using xarray plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2fc9aaa4", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# fig, ax = plt.subplots(figsize=(10, 4))\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "# fig.clear() # Remove old lines from plot and plot new one\n", + "# if len(ds[var].dims)==2:\n", + "# ds[var].plot(x=\"time\", add_colorbar=False)\n", + "# else:\n", + "# ds[var].plot()\n", + "# plt.grid()\n", + "# plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fabdd802", + "metadata": {}, + "outputs": [], + "source": [ + "# example 2: using act plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "962b4186", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "\n", + "\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "\n", + "# i_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + "# i_display.add_subplots((1,), figsize=(10, 4))\n", + "# ax = i_display.plot(var, subplot_index=(0,), set_title=f\"{var} ({ds_act[var].attrs['long_name']})\",)\n", + "\n", + "# ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "# ax.grid()\n", + "# plt.show()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/OZONE-AIR/aafo3.c1.ipynb b/VAPs/quicklook/OZONE-AIR/aafo3.c1.ipynb new file mode 100644 index 00000000..07c81973 --- /dev/null +++ b/VAPs/quicklook/OZONE-AIR/aafo3.c1.ipynb @@ -0,0 +1,297 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# AAFO3.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/ozone-air) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'aafo3'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2018-12-08', 'facility': 'F1', 'site': 'cor', 'start_date': '2018-11-04'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'cor', 'F1' )\n", + "\n", + "date_start = '2018-12-06'\n", + "date_end = '2018-12-08'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['o3']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'o3'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/PBLHT/.ipynb_checkpoints/pblhtsonde1mcfarl.c1-checkpoint.ipynb b/VAPs/quicklook/PBLHT/.ipynb_checkpoints/pblhtsonde1mcfarl.c1-checkpoint.ipynb new file mode 100644 index 00000000..b0b96878 --- /dev/null +++ b/VAPs/quicklook/PBLHT/.ipynb_checkpoints/pblhtsonde1mcfarl.c1-checkpoint.ipynb @@ -0,0 +1,679 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# PBLHTSONDE1MCFARL.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/pblht) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'pblhtsonde1mcfarl'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2017-01-02', 'facility': 'M1', 'site': 'awr', 'start_date': '2015-11-30'}, {'end_date': '2016-01-18', 'facility': 'S1', 'site': 'awr', 'start_date': '2015-12-01'}, {'end_date': '2017-10-31', 'facility': 'S1', 'site': 'asi', 'start_date': '2016-04-29'}, {'end_date': '2015-02-09', 'facility': 'M1', 'site': 'acx', 'start_date': '2015-01-12'}, {'end_date': '2020-05-31', 'facility': 'M1', 'site': 'anx', 'start_date': '2019-12-02'}, {'end_date': '2023-12-12', 'facility': 'C1', 'site': 'nsa', 'start_date': '2002-04-28'}, {'end_date': '2012-03-31', 'facility': 'M1', 'site': 'pgh', 'start_date': '2011-06-15'}, {'end_date': '2013-06-29', 'facility': 'M1', 'site': 'pvc', 'start_date': '2012-06-25'}, {'end_date': '2021-06-14', 'facility': 'M1', 'site': 'oli', 'start_date': '2013-10-03'}, {'end_date': '2020-10-01', 'facility': 'M1', 'site': 'mos', 'start_date': '2019-10-11'}, {'end_date': '2015-12-01', 'facility': 'M1', 'site': 'mao', 'start_date': '2014-01-01'}, {'end_date': '2012-04-08', 'facility': 'M1', 'site': 'gan', 'start_date': '2011-09-15'}, {'end_date': '2011-01-05', 'facility': 'M1', 'site': 'grw', 'start_date': '2009-04-16'}, {'end_date': '2013-10-03', 'facility': 'M1', 'site': 'mag', 'start_date': '2012-10-01'}, {'end_date': '2018-03-24', 'facility': 'M1', 'site': 'mar', 'start_date': '2017-10-31'}, {'end_date': '2023-06-15', 'facility': 'M1', 'site': 'guc', 'start_date': '2021-09-01'}, {'end_date': '2023-12-12', 'facility': 'M1', 'site': 'epc', 'start_date': '2023-02-06'}, {'end_date': '2008-12-28', 'facility': 'M1', 'site': 'hfe', 'start_date': '2008-05-14'}, {'end_date': '2019-04-29', 'facility': 'M1', 'site': 'cor', 'start_date': '2018-09-27'}, {'end_date': '2022-10-01', 'facility': 'M1', 'site': 'hou', 'start_date': '2021-09-18'}, {'end_date': '2022-09-25', 'facility': 'S1', 'site': 'hou', 'start_date': '2021-08-28'}, {'end_date': '2023-12-13', 'facility': 'C1', 'site': 'ena', 'start_date': '2013-09-28'}, {'end_date': '2008-01-01', 'facility': 'M1', 'site': 'fkb', 'start_date': '2007-03-24'}, {'end_date': '2007-01-08', 'facility': 'M1', 'site': 'nim', 'start_date': '2006-01-07'}, {'end_date': '2005-09-15', 'facility': 'M1', 'site': 'pye', 'start_date': '2005-02-25'}, {'end_date': '2011-04-24', 'facility': 'M1', 'site': 'sbs', 'start_date': '2010-11-08'}, {'end_date': '2007-06-13', 'facility': 'B1', 'site': 'sgp', 'start_date': '2002-05-13'}, {'end_date': '2007-06-29', 'facility': 'B4', 'site': 'sgp', 'start_date': '2002-05-20'}, {'end_date': '2007-06-22', 'facility': 'B5', 'site': 'sgp', 'start_date': '2002-05-20'}, {'end_date': '2002-11-26', 'facility': 'B6', 'site': 'sgp', 'start_date': '2001-06-20'}, {'end_date': '2023-12-11', 'facility': 'C1', 'site': 'sgp', 'start_date': '2001-04-01'}, {'end_date': '2014-09-12', 'facility': 'M1', 'site': 'tmp', 'start_date': '2014-02-01'}, {'end_date': '2014-07-07', 'facility': 'C1', 'site': 'twp', 'start_date': '2001-04-03'}, {'end_date': '2013-08-25', 'facility': 'C2', 'site': 'twp', 'start_date': '2001-04-01'}, {'end_date': '2015-01-14', 'facility': 'C3', 'site': 'twp', 'start_date': '2002-04-28'}]" + ] + }, + { + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "ac6764f5", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The following locations and date ranges are available for this VAP:\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
sitefacilitystart_dateend_date
0awrM12015-11-302017-01-02
1awrS12015-12-012016-01-18
2asiS12016-04-292017-10-31
3acxM12015-01-122015-02-09
4anxM12019-12-022020-05-31
5nsaC12002-04-282023-12-12
6pghM12011-06-152012-03-31
7pvcM12012-06-252013-06-29
8oliM12013-10-032021-06-14
9mosM12019-10-112020-10-01
10maoM12014-01-012015-12-01
11ganM12011-09-152012-04-08
12grwM12009-04-162011-01-05
13magM12012-10-012013-10-03
14marM12017-10-312018-03-24
15gucM12021-09-012023-06-15
16epcM12023-02-062023-12-12
17hfeM12008-05-142008-12-28
18corM12018-09-272019-04-29
19houM12021-09-182022-10-01
20houS12021-08-282022-09-25
21enaC12013-09-282023-12-13
22fkbM12007-03-242008-01-01
23nimM12006-01-072007-01-08
24pyeM12005-02-252005-09-15
25sbsM12010-11-082011-04-24
26sgpB12002-05-132007-06-13
27sgpB42002-05-202007-06-29
28sgpB52002-05-202007-06-22
29sgpB62001-06-202002-11-26
30sgpC12001-04-012023-12-11
31tmpM12014-02-012014-09-12
32twpC12001-04-032014-07-07
33twpC22001-04-012013-08-25
34twpC32002-04-282015-01-14
\n", + "
" + ], + "text/plain": [ + " site facility start_date end_date\n", + "0 awr M1 2015-11-30 2017-01-02\n", + "1 awr S1 2015-12-01 2016-01-18\n", + "2 asi S1 2016-04-29 2017-10-31\n", + "3 acx M1 2015-01-12 2015-02-09\n", + "4 anx M1 2019-12-02 2020-05-31\n", + "5 nsa C1 2002-04-28 2023-12-12\n", + "6 pgh M1 2011-06-15 2012-03-31\n", + "7 pvc M1 2012-06-25 2013-06-29\n", + "8 oli M1 2013-10-03 2021-06-14\n", + "9 mos M1 2019-10-11 2020-10-01\n", + "10 mao M1 2014-01-01 2015-12-01\n", + "11 gan M1 2011-09-15 2012-04-08\n", + "12 grw M1 2009-04-16 2011-01-05\n", + "13 mag M1 2012-10-01 2013-10-03\n", + "14 mar M1 2017-10-31 2018-03-24\n", + "15 guc M1 2021-09-01 2023-06-15\n", + "16 epc M1 2023-02-06 2023-12-12\n", + "17 hfe M1 2008-05-14 2008-12-28\n", + "18 cor M1 2018-09-27 2019-04-29\n", + "19 hou M1 2021-09-18 2022-10-01\n", + "20 hou S1 2021-08-28 2022-09-25\n", + "21 ena C1 2013-09-28 2023-12-13\n", + "22 fkb M1 2007-03-24 2008-01-01\n", + "23 nim M1 2006-01-07 2007-01-08\n", + "24 pye M1 2005-02-25 2005-09-15\n", + "25 sbs M1 2010-11-08 2011-04-24\n", + "26 sgp B1 2002-05-13 2007-06-13\n", + "27 sgp B4 2002-05-20 2007-06-29\n", + "28 sgp B5 2002-05-20 2007-06-22\n", + "29 sgp B6 2001-06-20 2002-11-26\n", + "30 sgp C1 2001-04-01 2023-12-11\n", + "31 tmp M1 2014-02-01 2014-09-12\n", + "32 twp C1 2001-04-03 2014-07-07\n", + "33 twp C2 2001-04-01 2013-08-25\n", + "34 twp C3 2002-04-28 2015-01-14" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'B1' )\n", + "\n", + "date_start = '2007-06-10'\n", + "date_end = '2007-06-12'" + ] + }, + { + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'/data/archive/sgp/sgppblhtsonde1mcfarlB1.c1'" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['20070610', '20070611', '20070612']" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "51feea2e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['/data/archive/sgp/sgppblhtsonde1mcfarlB1.c1/sgppblhtsonde1mcfarlB1.c1.20070611.150200.cdf',\n", + " '/data/archive/sgp/sgppblhtsonde1mcfarlB1.c1/sgppblhtsonde1mcfarlB1.c1.20070611.173000.cdf',\n", + " '/data/archive/sgp/sgppblhtsonde1mcfarlB1.c1/sgppblhtsonde1mcfarlB1.c1.20070611.202900.cdf',\n", + " '/data/archive/sgp/sgppblhtsonde1mcfarlB1.c1/sgppblhtsonde1mcfarlB1.c1.20070611.232800.cdf',\n", + " '/data/archive/sgp/sgppblhtsonde1mcfarlB1.c1/sgppblhtsonde1mcfarlB1.c1.20070612.052900.cdf',\n", + " '/data/archive/sgp/sgppblhtsonde1mcfarlB1.c1/sgppblhtsonde1mcfarlB1.c1.20070612.112900.cdf',\n", + " '/data/archive/sgp/sgppblhtsonde1mcfarlB1.c1/sgppblhtsonde1mcfarlB1.c1.20070612.143100.cdf',\n", + " '/data/archive/sgp/sgppblhtsonde1mcfarlB1.c1/sgppblhtsonde1mcfarlB1.c1.20070612.082900.cdf',\n", + " '/data/archive/sgp/sgppblhtsonde1mcfarlB1.c1/sgppblhtsonde1mcfarlB1.c1.20070612.233900.cdf',\n", + " '/data/archive/sgp/sgppblhtsonde1mcfarlB1.c1/sgppblhtsonde1mcfarlB1.c1.20070612.173100.cdf',\n", + " '/data/archive/sgp/sgppblhtsonde1mcfarlB1.c1/sgppblhtsonde1mcfarlB1.c1.20070612.023100.cdf']" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [ + { + "ename": "ValueError", + "evalue": "Coordinate variable height_ss is neither monotonically increasing nor monotonically decreasing on all datasets", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[0;32mIn[7], line 3\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[38;5;66;03m# Load files as a single dataset\u001b[39;00m\n\u001b[1;32m 2\u001b[0m files_list \u001b[38;5;241m=\u001b[39m files_filter \n\u001b[0;32m----> 3\u001b[0m ds \u001b[38;5;241m=\u001b[39m \u001b[43mact\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mio\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43marmfiles\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mread_netcdf\u001b[49m\u001b[43m(\u001b[49m\u001b[43mfiles_list\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 4\u001b[0m ds\u001b[38;5;241m.\u001b[39mclean\u001b[38;5;241m.\u001b[39mcleanup()\n\u001b[1;32m 5\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;132;01m{\u001b[39;00m\u001b[38;5;28mlen\u001b[39m(files_list)\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m files loaded\u001b[39m\u001b[38;5;124m'\u001b[39m)\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/act/io/armfiles.py:168\u001b[0m, in \u001b[0;36mread_netcdf\u001b[0;34m(filenames, concat_dim, return_None, combine, decode_times, use_cftime, use_base_time, combine_attrs, cleanup_qc, keep_variables, **kwargs)\u001b[0m\n\u001b[1;32m 164\u001b[0m ds \u001b[38;5;241m=\u001b[39m xr\u001b[38;5;241m.\u001b[39mopen_mfdataset(filenames, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs)\n\u001b[1;32m 166\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m 167\u001b[0m \u001b[38;5;66;03m# When all else fails raise the orginal exception\u001b[39;00m\n\u001b[0;32m--> 168\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m exception\n\u001b[1;32m 170\u001b[0m \u001b[38;5;66;03m# If requested use base_time and time_offset to derive time. Assumes that the units\u001b[39;00m\n\u001b[1;32m 171\u001b[0m \u001b[38;5;66;03m# of both are in seconds and that the value is number of seconds since epoch.\u001b[39;00m\n\u001b[1;32m 172\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m use_base_time:\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/act/io/armfiles.py:143\u001b[0m, in \u001b[0;36mread_netcdf\u001b[0;34m(filenames, concat_dim, return_None, combine, decode_times, use_cftime, use_base_time, combine_attrs, cleanup_qc, keep_variables, **kwargs)\u001b[0m\n\u001b[1;32m 139\u001b[0m except_tuple \u001b[38;5;241m=\u001b[39m except_tuple \u001b[38;5;241m+\u001b[39m (\u001b[38;5;167;01mFileNotFoundError\u001b[39;00m, \u001b[38;5;167;01mOSError\u001b[39;00m)\n\u001b[1;32m 141\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[1;32m 142\u001b[0m \u001b[38;5;66;03m# Read data file with Xarray function\u001b[39;00m\n\u001b[0;32m--> 143\u001b[0m ds \u001b[38;5;241m=\u001b[39m \u001b[43mxr\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mopen_mfdataset\u001b[49m\u001b[43m(\u001b[49m\u001b[43mfilenames\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 145\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m except_tuple \u001b[38;5;28;01mas\u001b[39;00m exception:\n\u001b[1;32m 146\u001b[0m \u001b[38;5;66;03m# If requested return None for File not found error\u001b[39;00m\n\u001b[1;32m 147\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mtype\u001b[39m(exception)\u001b[38;5;241m.\u001b[39m\u001b[38;5;18m__name__\u001b[39m \u001b[38;5;241m==\u001b[39m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mFileNotFoundError\u001b[39m\u001b[38;5;124m'\u001b[39m:\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/backends/api.py:1026\u001b[0m, in \u001b[0;36mopen_mfdataset\u001b[0;34m(paths, chunks, concat_dim, compat, preprocess, engine, data_vars, coords, combine, parallel, join, attrs_file, combine_attrs, **kwargs)\u001b[0m\n\u001b[1;32m 1013\u001b[0m combined \u001b[38;5;241m=\u001b[39m _nested_combine(\n\u001b[1;32m 1014\u001b[0m datasets,\n\u001b[1;32m 1015\u001b[0m concat_dims\u001b[38;5;241m=\u001b[39mconcat_dim,\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 1021\u001b[0m combine_attrs\u001b[38;5;241m=\u001b[39mcombine_attrs,\n\u001b[1;32m 1022\u001b[0m )\n\u001b[1;32m 1023\u001b[0m \u001b[38;5;28;01melif\u001b[39;00m combine \u001b[38;5;241m==\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mby_coords\u001b[39m\u001b[38;5;124m\"\u001b[39m:\n\u001b[1;32m 1024\u001b[0m \u001b[38;5;66;03m# Redo ordering from coordinates, ignoring how they were ordered\u001b[39;00m\n\u001b[1;32m 1025\u001b[0m \u001b[38;5;66;03m# previously\u001b[39;00m\n\u001b[0;32m-> 1026\u001b[0m combined \u001b[38;5;241m=\u001b[39m \u001b[43mcombine_by_coords\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 1027\u001b[0m \u001b[43m \u001b[49m\u001b[43mdatasets\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1028\u001b[0m \u001b[43m \u001b[49m\u001b[43mcompat\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcompat\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1029\u001b[0m \u001b[43m \u001b[49m\u001b[43mdata_vars\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mdata_vars\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1030\u001b[0m \u001b[43m \u001b[49m\u001b[43mcoords\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcoords\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1031\u001b[0m \u001b[43m \u001b[49m\u001b[43mjoin\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mjoin\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1032\u001b[0m \u001b[43m \u001b[49m\u001b[43mcombine_attrs\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcombine_attrs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1033\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 1034\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m 1035\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\n\u001b[1;32m 1036\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;132;01m{}\u001b[39;00m\u001b[38;5;124m is an invalid option for the keyword argument\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 1037\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m ``combine``\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;241m.\u001b[39mformat(combine)\n\u001b[1;32m 1038\u001b[0m )\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/core/combine.py:982\u001b[0m, in \u001b[0;36mcombine_by_coords\u001b[0;34m(data_objects, compat, data_vars, coords, fill_value, join, combine_attrs, datasets)\u001b[0m\n\u001b[1;32m 980\u001b[0m concatenated_grouped_by_data_vars \u001b[38;5;241m=\u001b[39m []\n\u001b[1;32m 981\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m \u001b[38;5;28mvars\u001b[39m, datasets_with_same_vars \u001b[38;5;129;01min\u001b[39;00m grouped_by_vars:\n\u001b[0;32m--> 982\u001b[0m concatenated \u001b[38;5;241m=\u001b[39m \u001b[43m_combine_single_variable_hypercube\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 983\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43mlist\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mdatasets_with_same_vars\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 984\u001b[0m \u001b[43m \u001b[49m\u001b[43mfill_value\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mfill_value\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 985\u001b[0m \u001b[43m \u001b[49m\u001b[43mdata_vars\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mdata_vars\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 986\u001b[0m \u001b[43m \u001b[49m\u001b[43mcoords\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcoords\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 987\u001b[0m \u001b[43m \u001b[49m\u001b[43mcompat\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcompat\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 988\u001b[0m \u001b[43m \u001b[49m\u001b[43mjoin\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mjoin\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 989\u001b[0m \u001b[43m \u001b[49m\u001b[43mcombine_attrs\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcombine_attrs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 990\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 991\u001b[0m concatenated_grouped_by_data_vars\u001b[38;5;241m.\u001b[39mappend(concatenated)\n\u001b[1;32m 993\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m merge(\n\u001b[1;32m 994\u001b[0m concatenated_grouped_by_data_vars,\n\u001b[1;32m 995\u001b[0m compat\u001b[38;5;241m=\u001b[39mcompat,\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 998\u001b[0m combine_attrs\u001b[38;5;241m=\u001b[39mcombine_attrs,\n\u001b[1;32m 999\u001b[0m )\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/core/combine.py:629\u001b[0m, in \u001b[0;36m_combine_single_variable_hypercube\u001b[0;34m(datasets, fill_value, data_vars, coords, compat, join, combine_attrs)\u001b[0m\n\u001b[1;32m 623\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mlen\u001b[39m(datasets) \u001b[38;5;241m==\u001b[39m \u001b[38;5;241m0\u001b[39m:\n\u001b[1;32m 624\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\n\u001b[1;32m 625\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mAt least one Dataset is required to resolve variable names \u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 626\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mfor combined hypercube.\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 627\u001b[0m )\n\u001b[0;32m--> 629\u001b[0m combined_ids, concat_dims \u001b[38;5;241m=\u001b[39m \u001b[43m_infer_concat_order_from_coords\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mlist\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mdatasets\u001b[49m\u001b[43m)\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 631\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m fill_value \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[1;32m 632\u001b[0m \u001b[38;5;66;03m# check that datasets form complete hypercube\u001b[39;00m\n\u001b[1;32m 633\u001b[0m _check_shape_tile_ids(combined_ids)\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/core/combine.py:116\u001b[0m, in \u001b[0;36m_infer_concat_order_from_coords\u001b[0;34m(datasets)\u001b[0m\n\u001b[1;32m 114\u001b[0m ascending \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mFalse\u001b[39;00m\n\u001b[1;32m 115\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[0;32m--> 116\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\n\u001b[1;32m 117\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mCoordinate variable \u001b[39m\u001b[38;5;132;01m{}\u001b[39;00m\u001b[38;5;124m is neither \u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 118\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mmonotonically increasing nor \u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 119\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mmonotonically decreasing on all datasets\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;241m.\u001b[39mformat(dim)\n\u001b[1;32m 120\u001b[0m )\n\u001b[1;32m 122\u001b[0m \u001b[38;5;66;03m# Assume that any two datasets whose coord along dim starts\u001b[39;00m\n\u001b[1;32m 123\u001b[0m \u001b[38;5;66;03m# with the same value have the same coord values throughout.\u001b[39;00m\n\u001b[1;32m 124\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28many\u001b[39m(index\u001b[38;5;241m.\u001b[39msize \u001b[38;5;241m==\u001b[39m \u001b[38;5;241m0\u001b[39m \u001b[38;5;28;01mfor\u001b[39;00m index \u001b[38;5;129;01min\u001b[39;00m indexes):\n", + "\u001b[0;31mValueError\u001b[0m: Coordinate variable height_ss is neither monotonically increasing nor monotonically decreasing on all datasets" + ] + } + ], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['pbl_height_heffter', 'pbl_height_liu_liang', 'pbl_height_bulk_richardson_pt25']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'pbl_height_heffter'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/PBLHT/PBLHT_tutorial.ipynb b/VAPs/quicklook/PBLHT/PBLHT_tutorial.ipynb new file mode 100644 index 00000000..8594a837 --- /dev/null +++ b/VAPs/quicklook/PBLHT/PBLHT_tutorial.ipynb @@ -0,0 +1,838 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# PBLHTSONDE1MCFARL.C1 Notebook\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/pblht) for more information about this vap." + ] + }, + { + "cell_type": "markdown", + "id": "97097763", + "metadata": {}, + "source": [ + "In this notebook, we demonstrate the workflow to explore ARM vap data (using pblhtsonde1mcfarl as an example.) Value-added products (VAPs) are higher-order data products that have been analyzed and processed to ease scientists’ use of ARM data in atmospheric research and global climate models. \n", + "Here is the main content we will cover." + ] + }, + { + "cell_type": "markdown", + "id": "eddec40f", + "metadata": {}, + "source": [ + "# Table of Content\n", + "## Access the data\n", + "* How to retrieve the data\n", + "* Data path and file name conventions\n", + "* Load data\n", + "## Explore the data\n", + "* NetCDF Data structure\n", + "* Xarray essentials\n", + "* Xarray Variable\n", + "## Plot the data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "import random\n", + "\n", + "import glob\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "2372d5be", + "metadata": {}, + "source": [ + "## Access the data" + ] + }, + { + "cell_type": "markdown", + "id": "5b0d9684", + "metadata": {}, + "source": [ + "### How to retrieve the data\n", + "We assume the path \"/data/archive\" is available where you are running this notebook. You can use `os.path.exists(\"/data/archive\")` to verify if the path exists at your machine.\n", + "\n", + "\n", + "### Data path and file name conventions\n", + "There several common terminologies regarding ARM data, for example, data-stream-name, data-level, site, facility, instrument, etc. (For more details, please see [ARM Data File Standards Version 1.2](https://www.google.com/search?q=arm+datastream+facility+cite+definition&rlz=1C1GCEJ_enUS1029US1029&ei=hb41ZICrBPukqtsPvMODIA&ved=0ahUKEwjAgeiS0aL-AhV7kmoFHbzhAAQQ4dUDCBA&uact=5&oq=arm+datastream+facility+cite+definition&gs_lcp=Cgxnd3Mtd2l6LXNlcnAQAzIFCAAQogQyBQgAEKIEMgUIABCiBDIFCAAQogQyBQgAEKIEOgoIABBHENYEELADOgoIIRCgARDDBBAKSgQIQRgAUJ4DWIoOYO0PaAFwAXgAgAGMAYgBuQmSAQMyLjmYAQCgAQHIAQjAAQE&sclient=gws-wiz-serp).) \n", + "\n", + "For example, this notebook is called `pblhtsonde1mcfarl.c1`, where `pblhtsonde1mcfarl` is the \"datastream name\", and the `{process.ds_class_level}}` is the \"data level\".\n", + "\n", + "This datastream also contains site `awr` and facility `M1`. (Note: individual datastream might have multiple site-facility pairs.)\n", + "In such a case, the data of this data-stream is stored at `/data/archive/awr/awrpblhtsonde1mcfarlM1.c1`, which is in the format of `//.`. We can use the following method to assign the data-stream directory `datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )`\n", + "\n", + "The data files under datastream_dir also follows naming conventions. But once reach the datastream_dir level, the most import file naming convention to differentiate the files is \"yyyyMMdd.hhmmss\", which comes handy to filter out files based on datetime. For example, we can use `glob.glob(f'{datastream_dir}/*.200709*.*')` to filter files in 2007 September.\n", + "\n", + "(Note: refer to the https://adc.arm.gov/discovery/#/ and https://adc.arm.gov/solr8/metadata/select API to explore ARM datastream and assoicated available sites and facilities\n", + "\n", + "Please see the following examples in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d7e9eb85", + "metadata": {}, + "outputs": [], + "source": [ + "# Verify if DATA_DIR path exists\n", + "DATA_DIR = \"/data/archive\"\n", + "os.path.exists(DATA_DIR)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "586993fd", + "metadata": {}, + "outputs": [], + "source": [ + "# Speicify datastream_dir following the path conventions and check its existence\n", + "DATASTREAM_NAME = \"pblhtsonde1mcfarl\"\n", + "DATA_LEVEL = \"c1\"\n", + "site = \"awr\"\n", + "facility = \"M1\"\n", + "datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )\n", + "print(datastream_dir)\n", + "print(os.path.exists(datastream_dir))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0742f7c1", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: list 5 (random) files under datastream_dir\n", + "files = os.listdir(datastream_dir)\n", + "files[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39b98a36", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: get most recent file\n", + "list_of_files = glob.glob(f\"{datastream_dir}/*\") # * means all if need specific format then *.csv\n", + "latest_file = max(list_of_files, key=os.path.getctime)\n", + "latest_file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "902d514e", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus sort datastream files based on datetime\n", + "files = os.listdir(datastream_dir)\n", + "file_sorted = files.copy()\n", + "file_sorted.sort() \n", + "print(file_sorted[:5])\n", + "\n", + "# to reverse\n", + "file_sorted_reverse = files.copy() \n", + "file_sorted_reverse.sort(reverse=True)\n", + "print(file_sorted_reverse[:5])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec5923b2", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: pattern matching\n", + "# filter the 200709** files under datastream_dir\n", + "files_filter = glob.glob(f'{datastream_dir}/*.200709*.*')\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c4a4aa18", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: use armnotebook_utils.file_filter (TODO) to filter files based on datastream info \n" + ] + }, + { + "cell_type": "markdown", + "id": "cfeb9efc", + "metadata": {}, + "source": [ + "### Load data\n", + "The arm data is stored in [NetCDF](https://en.wikipedia.org/wiki/NetCDF#:~:text=%22NetCDF%20(network%20Common%20Data%20Form,format%20for%20representing%20scientific%20data.) format. We can use xarray's `open_dataset` method to load single file, or `open_mfdataset` to open multiple files. (Note: the latter will still return a single xarray dataset object by combining multiple files.)\n", + "\n", + "Note: open_dataset keeps the file handle open and lazy loads its contents. All parameters are passed directly to open_dataset. It is a preferable method over load_dataset for memory-effiency. (more details, please see [xarray.open_dataset](https://docs.xarray.dev/en/stable/generated/xarray.open_dataset.html))\n", + "\n", + "See the following example in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a440a329", + "metadata": {}, + "outputs": [], + "source": [ + "# open single file\n", + "full_path = latest_file\n", + "print(full_path)\n", + "ds_single = xr.open_dataset(full_path)\n", + "ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0143a3d", + "metadata": {}, + "outputs": [], + "source": [ + "print(ds_single)\n", + "print(type(ds_single))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c0f8939", + "metadata": {}, + "outputs": [], + "source": [ + "# open multiple files\n", + "n_files = 3\n", + "full_paths = [os.path.join(datastream_dir, f_path) for f_path in file_sorted_reverse[:n_files]]\n", + "print(full_paths)\n", + "try: # Note: sometimes multiple files cannot be merged, so we used try except here.\n", + " ds_mutiple = xr.open_mfdataset(full_paths)\n", + " # ds_mutiple\n", + " print(type(ds_mutiple))\n", + " print(ds_mutiple)\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "3fa59943", + "metadata": {}, + "source": [ + "## Explore the data" + ] + }, + { + "cell_type": "markdown", + "id": "571f69b9", + "metadata": {}, + "source": [ + "### NetCDF Data structure \n", + "\n", + "\n", + "(If you are confident with NetCDF basics and xarray essentials, feel free to skip this session.)\n", + "Before we dive into data exploration, there are some eseential concpets we should be familiar with (shown below): \n", + "* Dataset\n", + "* Data array\n", + "* Variable\n", + "* Dimenssion\n", + "* Coordinate\n", + "* Data Type\n", + "* Meta Data (Attributes)\n", + "\n", + "We will not go into details about NetCDF basics and here are some references you might find helpful\n", + "* [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#)\n", + "* [Network Common Data Form (NetCDF)](https://www.unidata.ucar.edu/software/netcdf/) and [A Brief History of (netCDF) Time](https://www.unidata.ucar.edu/software/netcdf/time/recs.html)\n", + "\n", + "\n", + "\n", + "### Xarray essentials\n", + "Earlier we introduced the xarray pacakge and used `open_dataset` and `open_mfdataset` to retrieve NetCDF data file as an xarray Dataset object. Recall that you can review the data in a notebook by using `print(ds)` or `ds`. \n", + "\n", + "Using xarray to retrieve the aforementioned NetCDF basics is straightforwared. In general,\n", + "* Dataset: `ds`\n", + "* Data array: `ds.variables`\n", + "* Variable: `ds.ds.variables`\n", + "* Dimenssion: `ds.dims`\n", + "* Coordinate: `ds.coords`\n", + "* Data Type: `type`\n", + "* Meta Data (Attributes)\n", + "\n", + "Also, here are some references if you are new to xarray\n", + "* [Xarray in 45 minutes](https://tutorial.xarray.dev/overview/xarray-in-45-min.html)\n", + "* [Handling NetCDF Files using XArray for Absolute Beginners](https://towardsdatascience.com/handling-netcdf-files-using-xarray-for-absolute-beginners-111a8ab4463f)\n", + "\n", + "Try the following commands in action\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b3eac323", + "metadata": {}, + "outputs": [], + "source": [ + "ds = ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77ecf85d", + "metadata": {}, + "outputs": [], + "source": [ + "# Dataset \n", + "ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25e7de09", + "metadata": {}, + "outputs": [], + "source": [ + "# Data array, Variable\n", + "# Note: the info can be overwhelming. Do not attempt to grasp everything at the first glance.\n", + "ds.variables" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c41b67e", + "metadata": {}, + "outputs": [], + "source": [ + "# dimenssions\n", + "ds.dims" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "156f1dfc", + "metadata": {}, + "outputs": [], + "source": [ + "# coordinates\n", + "ds.coords" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "277d6064", + "metadata": {}, + "outputs": [], + "source": [ + "# Meta Data (Attributes)\n", + "ds.attrs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d334681f", + "metadata": {}, + "outputs": [], + "source": [ + "# type\n", + "print(type(ds))\n", + "print(type(ds.variables))\n", + "print(type(ds.dims))\n", + "print(type(ds.coords))\n", + "print(type(ds.attrs))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "643399d6", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: retrieve variable names only\n", + "list(ds.variables)" + ] + }, + { + "cell_type": "markdown", + "id": "1cf36878", + "metadata": {}, + "source": [ + "#### Discussion: variable vs. coordinates vs. dimenssions. \n", + "From [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#):\n", + "\n", + "* Dimession: A dimension may be used to represent a real physical dimension, for example, time, latitude, longitude, or height. A dimension might also be used to index other quantities, for example station or model-run-number.\n", + "A netCDF dimension has both a name and a length. A dimension length is an arbitrary positive integer, except that one dimension in a netCDF dataset can have the length UNLIMITED.\n", + "\n", + "* Variables: Variables are used to store the bulk of the data in a netCDF dataset. A variable represents an array of values of the same type. A scalar value is treated as a 0-dimensional array. A variable has a name, a data type, and a shape described by its list of dimensions specified when the variable is created. A variable may also have associated attributes, which may be added, deleted or changed after the variable is created.\n", + "\n", + "* Coordinate (Variables): It is legal for a variable to have the same name as a dimension. Such variables have no special meaning to the netCDF library. However there is a convention that such variables should be treated in a special way by software using this library.\n", + "A variable with the same name as a dimension is called a coordinate variable. It typically defines a physical coordinate corresponding to that dimension.\n", + "\n", + "\n", + "Tips: By definition, coordinates and dimenssions are also variable. For example ds.time is a coordinate but it is also a special variable. But in practice, when we talk about variable, it implies regular/non-coordinate variable. We will use this convention for the remaining notebook. " + ] + }, + { + "cell_type": "markdown", + "id": "d47120f9", + "metadata": {}, + "source": [ + "### Xarray Variable\n", + "\n", + "\n", + "We can use `ds.variables` to access varialbes of a dataset. But many times viewing print-out the whole datasets can be overwhemling. Instead, we would work on the individual variable (also called Xarray data array.) using the `ds[var_name]` syntax, where `var_name` is the name of the variable. See the following examples in action.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5a57e136", + "metadata": {}, + "outputs": [], + "source": [ + "print(type(ds[\"time\"]))\n", + "ds[\"time\"]" + ] + }, + { + "cell_type": "markdown", + "id": "51ec643e", + "metadata": {}, + "source": [ + "#### Variable properties\n", + "For individual varible, the following properties are mostly used with the assocaited xarray method to retrieve them. (assuming var = ds[var_name])\n", + "* name: `var.name`\n", + "* data content: `var.data`\n", + "* attributes: `var.attrs`\n", + "* dimenstions: `var.dims`\n", + "* data type: `var.data.dtype`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a632a525", + "metadata": {}, + "outputs": [], + "source": [ + "var_name = \"time\"\n", + "var = ds[var_name]\n", + "\n", + "print(\"var.name: \\n\", var.name, \"\\n\")\n", + "print(\"var.data: \\n\", var.data, \"\\n\")\n", + "print(\"var.attrs: \\n\", var.attrs, \"\\n\")\n", + "print(\"var.dims: \\n\", var.dims, \"\\n\")\n", + "print(\"var.data.dtype: \\n\", var.data.dtype, \"\\n\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b256b07f", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: indivdual variable info table (collect and display variable info as a tabular format)\n", + "df_info = pd.DataFrame()\n", + "df_info[\"var_name\"] = list(ds.variables)\n", + "df_info[\"dims\"] = df_info.var_name.apply(lambda x: ds[x].dims)\n", + "df_info[\"is_dim\"] = df_info.var_name.apply(lambda x: x in ds.dims)\n", + "df_info[\"n_dim\"] = df_info.var_name.apply(lambda x: len(ds[x].dims))\n", + "df_info[\"attrs\"] = df_info.var_name.apply(lambda x: ds[x].attrs)\n", + "df_info[\"dtype\"] = df_info.var_name.apply(lambda x: ds[x].data.dtype)\n", + "df_info" + ] + }, + { + "cell_type": "markdown", + "id": "e6a36b96", + "metadata": {}, + "source": [ + "### Data cleaning/Preprocessing (skipped)\n", + "Data cleaning and preprocessing is an important stage in the the data analysis pipeline. However it will be out of the scope of this notebook. For more information about data cleaning and preprocessing basics, here are some references. \n", + "* [Xarray Fundamentals](https://earth-env-data-science.github.io/lectures/xarray/xarray.html)\n", + "* [Xarray Tutorial — Pangeo Gallery documentation](http://gallery.pangeo.io/repos/pangeo-data/pangeo-tutorial-gallery/xarray.html)\n", + "* [Pythonic Data Cleaning With pandas and NumPy](https://realpython.com/python-data-cleaning-numpy-pandas/)\n", + "* [Pandas - Cleaning Data](https://www.w3schools.com/python/pandas/pandas_cleaning.asp)" + ] + }, + { + "cell_type": "markdown", + "id": "ae6e1a69", + "metadata": {}, + "source": [ + "### Plotting\n", + "Data visualization (or plotting) is another important data analysis topic and deserves its own discussion. Here in this notebook we will only demonstrate basic tool for simple data visualization tasks.\n", + "\n", + "Here are some reference you might find useful:\n", + "* xarray plotting: https://docs.xarray.dev/en/stable/user-guide/plotting.html\n", + "* Atmospheric Community Toolkit (ACT): https://arm-doe.github.io/ACT/index.html\n", + "\n", + "Note: this notebook is auto-generated using a template. It uses a general idea to select variable(s) to plot and is not customized to each indivdual notebook. Feel free to change the variables in intrrests, especially certain figure is failed to plot." + ] + }, + { + "cell_type": "markdown", + "id": "7e417fb2", + "metadata": {}, + "source": [ + "#### 1-dimenssional basic time series plot\n", + "\n", + "For the following plot we would like to find variables such that\n", + "* it has one and only one dimession\n", + "* \"time\" is its coordinate variable\n", + "* it is not a dimenssion itself,\n", + "* it is not a special variable with substrings within [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "\n", + "Please see the following example in action. (For more details about pandas filtering, please see the following references.)\n", + "* [pandas: multiple conditions while indexing data frame](https://stackoverflow.com/questions/22591174/pandas-multiple-conditions-while-indexing-data-frame-unexpected-behavior)\n", + "* [pandas.DataFrame.apply](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.apply.html)\n", + "* [How to test if a string contains one of the substrings in a list...](https://stackoverflow.com/questions/26577516/how-to-test-if-a-string-contains-one-of-the-substrings-in-a-list-in-pandas" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30edac2d", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==1, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter = df_info[(df_info.n_dim == 1) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4777a659", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_1d = df_filter.var_name.values[0]\n", + " var_1d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb5ae985", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " ds[var_1d].plot()\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "9e41066e", + "metadata": {}, + "source": [ + "#### 2-dimenssional basic plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09bd4adc", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==2, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_2 = df_info[(df_info.n_dim == 2) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2cdea1a9", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_2d = df_filter_2.var_name.values[0]\n", + " var_2d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5f48879", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " print(ds[var_2d].dims)\n", + " # ds[var_2d].plot()\n", + "\n", + " # conventionally, use \"time\" as x-axis\n", + " ds[var_2d].plot(x=\"time\")\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "7554b2a6", + "metadata": {}, + "source": [ + "#### qc-plotting (optional)\n", + "\n", + "Note: act qc plotting has more strict requirement, one of them is the associated qc variable need to have \"flag_masks\" attributes. Which is added by using ds = act.io.armfiles.read_netcdf(files_list), then ds.clean.cleanup()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71c2096f", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " ds_act = act.io.armfiles.read_netcdf(full_path)\n", + " print(type(ds_act))\n", + " ds_act.clean.cleanup()\n", + "\n", + " # or \n", + " # ds.clean.cleanup()\n", + "except Exception as e:\n", + " print(\"ERROR\", e)\n", + " ds_act = ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "844f8505", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter valid variables for ACT qc plotting\n", + "condition = (\"qc_\" + df_info.var_name).apply(lambda x: ds_act[x].attrs.get(\"flag_masks\") is not None \n", + " if x in list(ds.variables) else False)\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_3 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings))) &\n", + " condition\n", + " ]\n", + "df_filter_3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd38cc6", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "try:\n", + " qc_variable = df_filter_3.var_name.values[0]\n", + " print(qc_variable)\n", + "except Exception as e:\n", + " print(e)\n", + " \n", + "try:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + " qc_ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "d76e4d27", + "metadata": {}, + "source": [ + "#### bonus: choose variables to plot from a dropdown menu " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb733804", + "metadata": {}, + "outputs": [], + "source": [ + "# Valid variables filtering\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_4 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82d5c20d", + "metadata": {}, + "outputs": [], + "source": [ + "# example 1: using xarray plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2fc9aaa4", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# fig, ax = plt.subplots(figsize=(10, 4))\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "# fig.clear() # Remove old lines from plot and plot new one\n", + "# if len(ds[var].dims)==2:\n", + "# ds[var].plot(x=\"time\", add_colorbar=False)\n", + "# else:\n", + "# ds[var].plot()\n", + "# plt.grid()\n", + "# plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fabdd802", + "metadata": {}, + "outputs": [], + "source": [ + "# example 2: using act plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "962b4186", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "\n", + "\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "\n", + "# i_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + "# i_display.add_subplots((1,), figsize=(10, 4))\n", + "# ax = i_display.plot(var, subplot_index=(0,), set_title=f\"{var} ({ds_act[var].attrs['long_name']})\",)\n", + "\n", + "# ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "# ax.grid()\n", + "# plt.show()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/PBLHT/pblhtsonde1mcfarl.c1.ipynb b/VAPs/quicklook/PBLHT/pblhtsonde1mcfarl.c1.ipynb new file mode 100644 index 00000000..b10fdef4 --- /dev/null +++ b/VAPs/quicklook/PBLHT/pblhtsonde1mcfarl.c1.ipynb @@ -0,0 +1,297 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# PBLHTSONDE1MCFARL.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/pblht) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'pblhtsonde1mcfarl'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2017-01-02', 'facility': 'M1', 'site': 'awr', 'start_date': '2015-11-30'}, {'end_date': '2016-01-18', 'facility': 'S1', 'site': 'awr', 'start_date': '2015-12-01'}, {'end_date': '2017-10-31', 'facility': 'S1', 'site': 'asi', 'start_date': '2016-04-29'}, {'end_date': '2020-05-31', 'facility': 'M1', 'site': 'anx', 'start_date': '2019-12-02'}, {'end_date': '2015-02-09', 'facility': 'M1', 'site': 'acx', 'start_date': '2015-01-12'}, {'end_date': '2021-06-14', 'facility': 'M1', 'site': 'oli', 'start_date': '2013-10-03'}, {'end_date': '2023-06-15', 'facility': 'M1', 'site': 'guc', 'start_date': '2021-09-01'}, {'end_date': '2023-12-19', 'facility': 'C1', 'site': 'ena', 'start_date': '2013-09-28'}, {'end_date': '2022-10-01', 'facility': 'M1', 'site': 'hou', 'start_date': '2021-09-18'}, {'end_date': '2022-09-25', 'facility': 'S1', 'site': 'hou', 'start_date': '2021-08-28'}, {'end_date': '2023-12-18', 'facility': 'M1', 'site': 'epc', 'start_date': '2023-02-06'}, {'end_date': '2008-01-01', 'facility': 'M1', 'site': 'fkb', 'start_date': '2007-03-24'}, {'end_date': '2015-12-01', 'facility': 'M1', 'site': 'mao', 'start_date': '2014-01-01'}, {'end_date': '2018-03-24', 'facility': 'M1', 'site': 'mar', 'start_date': '2017-10-31'}, {'end_date': '2008-12-28', 'facility': 'M1', 'site': 'hfe', 'start_date': '2008-05-14'}, {'end_date': '2019-04-29', 'facility': 'M1', 'site': 'cor', 'start_date': '2018-09-27'}, {'end_date': '2013-10-03', 'facility': 'M1', 'site': 'mag', 'start_date': '2012-10-01'}, {'end_date': '2012-04-08', 'facility': 'M1', 'site': 'gan', 'start_date': '2011-09-15'}, {'end_date': '2011-01-05', 'facility': 'M1', 'site': 'grw', 'start_date': '2009-04-16'}, {'end_date': '2013-06-29', 'facility': 'M1', 'site': 'pvc', 'start_date': '2012-06-25'}, {'end_date': '2005-09-15', 'facility': 'M1', 'site': 'pye', 'start_date': '2005-02-25'}, {'end_date': '2011-04-24', 'facility': 'M1', 'site': 'sbs', 'start_date': '2010-11-08'}, {'end_date': '2020-10-01', 'facility': 'M1', 'site': 'mos', 'start_date': '2019-10-11'}, {'end_date': '2012-03-31', 'facility': 'M1', 'site': 'pgh', 'start_date': '2011-06-15'}, {'end_date': '2023-12-18', 'facility': 'C1', 'site': 'nsa', 'start_date': '2002-04-28'}, {'end_date': '2007-01-08', 'facility': 'M1', 'site': 'nim', 'start_date': '2006-01-07'}, {'end_date': '2007-06-13', 'facility': 'B1', 'site': 'sgp', 'start_date': '2002-05-13'}, {'end_date': '2007-06-29', 'facility': 'B4', 'site': 'sgp', 'start_date': '2002-05-20'}, {'end_date': '2007-06-22', 'facility': 'B5', 'site': 'sgp', 'start_date': '2002-05-20'}, {'end_date': '2002-11-26', 'facility': 'B6', 'site': 'sgp', 'start_date': '2001-06-20'}, {'end_date': '2023-12-18', 'facility': 'C1', 'site': 'sgp', 'start_date': '2001-04-01'}, {'end_date': '2014-09-12', 'facility': 'M1', 'site': 'tmp', 'start_date': '2014-02-01'}, {'end_date': '2014-07-07', 'facility': 'C1', 'site': 'twp', 'start_date': '2001-04-03'}, {'end_date': '2013-08-25', 'facility': 'C2', 'site': 'twp', 'start_date': '2001-04-01'}, {'end_date': '2015-01-14', 'facility': 'C3', 'site': 'twp', 'start_date': '2002-04-28'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'B1' )\n", + "\n", + "date_start = '2007-06-10'\n", + "date_end = '2007-06-12'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['pbl_height_heffter', 'pbl_height_liu_liang', 'pbl_height_bulk_richardson_pt25']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'pbl_height_heffter'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/PBLHT/pblhtsondeyr1mcfarl.c1.ipynb b/VAPs/quicklook/PBLHT/pblhtsondeyr1mcfarl.c1.ipynb new file mode 100644 index 00000000..f5f5858e --- /dev/null +++ b/VAPs/quicklook/PBLHT/pblhtsondeyr1mcfarl.c1.ipynb @@ -0,0 +1,339 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# PBLHTSONDEYR1MCFARL.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/pblht) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'pblhtsondeyr1mcfarl'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2016-12-30', 'facility': 'M1', 'site': 'awr', 'start_date': '2015-11-30'}, {'end_date': '2016-01-16', 'facility': 'S1', 'site': 'awr', 'start_date': '2015-12-01'}, {'end_date': '2016-12-31', 'facility': 'S1', 'site': 'asi', 'start_date': '2016-04-29'}, {'end_date': '2015-02-08', 'facility': 'M1', 'site': 'acx', 'start_date': '2015-01-12'}, {'end_date': '2021-06-13', 'facility': 'M1', 'site': 'oli', 'start_date': '2014-02-28'}, {'end_date': '2021-12-31', 'facility': 'C1', 'site': 'ena', 'start_date': '2013-09-28'}, {'end_date': '2022-09-30', 'facility': 'M1', 'site': 'hou', 'start_date': '2021-09-18'}, {'end_date': '2022-09-25', 'facility': 'S1', 'site': 'hou', 'start_date': '2021-08-28'}, {'end_date': '2007-12-31', 'facility': 'M1', 'site': 'fkb', 'start_date': '2007-03-24'}, {'end_date': '2014-12-31', 'facility': 'M1', 'site': 'mao', 'start_date': '2014-01-01'}, {'end_date': '2018-03-23', 'facility': 'M1', 'site': 'mar', 'start_date': '2017-10-31'}, {'end_date': '2008-12-28', 'facility': 'M1', 'site': 'hfe', 'start_date': '2008-05-14'}, {'end_date': '2019-04-29', 'facility': 'M1', 'site': 'cor', 'start_date': '2018-09-27'}, {'end_date': '2013-10-01', 'facility': 'M1', 'site': 'mag', 'start_date': '2012-10-01'}, {'end_date': '2010-03-15', 'facility': 'M1', 'site': 'grw', 'start_date': '2009-04-16'}, {'end_date': '2005-09-15', 'facility': 'M1', 'site': 'pye', 'start_date': '2005-02-25'}, {'end_date': '2020-09-16', 'facility': 'M1', 'site': 'mos', 'start_date': '2020-07-31'}, {'end_date': '2012-03-31', 'facility': 'M1', 'site': 'pgh', 'start_date': '2011-06-15'}, {'end_date': '2021-12-31', 'facility': 'C1', 'site': 'nsa', 'start_date': '2002-04-28'}, {'end_date': '2006-12-20', 'facility': 'M1', 'site': 'nim', 'start_date': '2006-01-07'}, {'end_date': '2007-06-12', 'facility': 'B1', 'site': 'sgp', 'start_date': '2002-05-13'}, {'end_date': '2007-06-29', 'facility': 'B4', 'site': 'sgp', 'start_date': '2002-05-20'}, {'end_date': '2007-06-21', 'facility': 'B5', 'site': 'sgp', 'start_date': '2002-05-20'}, {'end_date': '2002-11-25', 'facility': 'B6', 'site': 'sgp', 'start_date': '2001-06-20'}, {'end_date': '2021-12-31', 'facility': 'C1', 'site': 'sgp', 'start_date': '2001-04-01'}, {'end_date': '2013-12-02', 'facility': 'C1', 'site': 'twp', 'start_date': '2001-04-03'}, {'end_date': '2013-08-24', 'facility': 'C2', 'site': 'twp', 'start_date': '2001-04-01'}, {'end_date': '2015-01-13', 'facility': 'C3', 'site': 'twp', 'start_date': '2002-04-28'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'B1' )\n", + "\n", + "date_start = '2007-06-03'\n", + "date_end = '2007-06-05'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['pbl_height_heffter', 'pbl_height_liu_liang', 'pbl_height_bulk_richardson_pt25']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'pbl_height_heffter'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'pbl_height_heffter'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/PCCP/PCCP_tutorial.ipynb b/VAPs/quicklook/PCCP/PCCP_tutorial.ipynb new file mode 100644 index 00000000..e8b0ca67 --- /dev/null +++ b/VAPs/quicklook/PCCP/PCCP_tutorial.ipynb @@ -0,0 +1,943 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# PCCP.C1 Notebook\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/pccp) for more information about this vap." + ] + }, + { + "cell_type": "markdown", + "id": "97097763", + "metadata": {}, + "source": [ + "In this notebook, we demonstrate the workflow to explore ARM vap data (using pccp as an example.) Value-added products (VAPs) are higher-order data products that have been analyzed and processed to ease scientists’ use of ARM data in atmospheric research and global climate models. \n", + "Here is the main content we will cover." + ] + }, + { + "cell_type": "markdown", + "id": "eddec40f", + "metadata": {}, + "source": [ + "# Table of Content\n", + "## Access the data\n", + "* How to retrieve the data\n", + "* Data path and file name conventions\n", + "* Load data\n", + "## Explore the data\n", + "* NetCDF Data structure\n", + "* Xarray essentials\n", + "* Xarray Variable\n", + "## Plot the data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "import random\n", + "\n", + "import glob\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "2372d5be", + "metadata": {}, + "source": [ + "## Access the data" + ] + }, + { + "cell_type": "markdown", + "id": "5b0d9684", + "metadata": {}, + "source": [ + "### How to retrieve the data\n", + "We assume the path \"/data/archive\" is available where you are running this notebook. You can use `os.path.exists(\"/data/archive\")` to verify if the path exists at your machine.\n", + "\n", + "\n", + "### Data path and file name conventions\n", + "There several common terminologies regarding ARM data, for example, data-stream-name, data-level, site, facility, instrument, etc. (For more details, please see [ARM Data File Standards Version 1.2](https://www.google.com/search?q=arm+datastream+facility+cite+definition&rlz=1C1GCEJ_enUS1029US1029&ei=hb41ZICrBPukqtsPvMODIA&ved=0ahUKEwjAgeiS0aL-AhV7kmoFHbzhAAQQ4dUDCBA&uact=5&oq=arm+datastream+facility+cite+definition&gs_lcp=Cgxnd3Mtd2l6LXNlcnAQAzIFCAAQogQyBQgAEKIEMgUIABCiBDIFCAAQogQyBQgAEKIEOgoIABBHENYEELADOgoIIRCgARDDBBAKSgQIQRgAUJ4DWIoOYO0PaAFwAXgAgAGMAYgBuQmSAQMyLjmYAQCgAQHIAQjAAQE&sclient=gws-wiz-serp).) \n", + "\n", + "For example, this notebook is called `pccp.c1`, where `pccp` is the \"datastream name\", and the `{process.ds_class_level}}` is the \"data level\".\n", + "\n", + "This datastream also contains site `hou` and facility `S5`. (Note: individual datastream might have multiple site-facility pairs.)\n", + "In such a case, the data of this data-stream is stored at `/data/archive/hou/houpccpS5.c1`, which is in the format of `//.`. We can use the following method to assign the data-stream directory `datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )`\n", + "\n", + "The data files under datastream_dir also follows naming conventions. But once reach the datastream_dir level, the most import file naming convention to differentiate the files is \"yyyyMMdd.hhmmss\", which comes handy to filter out files based on datetime. For example, we can use `glob.glob(f'{datastream_dir}/*.200709*.*')` to filter files in 2007 September.\n", + "\n", + "(Note: refer to the https://adc.arm.gov/discovery/#/ and https://adc.arm.gov/solr8/metadata/select API to explore ARM datastream and assoicated available sites and facilities\n", + "\n", + "Please see the following examples in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d7e9eb85", + "metadata": {}, + "outputs": [], + "source": [ + "# Verify if DATA_DIR path exists\n", + "DATA_DIR = \"/data/archive\"\n", + "os.path.exists(DATA_DIR)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "586993fd", + "metadata": {}, + "outputs": [], + "source": [ + "# Speicify datastream_dir following the path conventions and check its existence\n", + "DATASTREAM_NAME = \"pccp\"\n", + "DATA_LEVEL = \"c1\"\n", + "site = \"hou\"\n", + "facility = \"S5\"\n", + "datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )\n", + "print(datastream_dir)\n", + "print(os.path.exists(datastream_dir))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0742f7c1", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: list 5 (random) files under datastream_dir\n", + "files = os.listdir(datastream_dir)\n", + "files[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39b98a36", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: get most recent file\n", + "list_of_files = glob.glob(f\"{datastream_dir}/*\") # * means all if need specific format then *.csv\n", + "latest_file = max(list_of_files, key=os.path.getctime)\n", + "latest_file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "902d514e", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus sort datastream files based on datetime\n", + "files = os.listdir(datastream_dir)\n", + "file_sorted = files.copy()\n", + "file_sorted.sort() \n", + "print(file_sorted[:5])\n", + "\n", + "# to reverse\n", + "file_sorted_reverse = files.copy() \n", + "file_sorted_reverse.sort(reverse=True)\n", + "print(file_sorted_reverse[:5])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec5923b2", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: pattern matching\n", + "# filter the 200709** files under datastream_dir\n", + "files_filter = glob.glob(f'{datastream_dir}/*.200709*.*')\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c4a4aa18", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: use armnotebook_utils.file_filter (TODO) to filter files based on datastream info \n" + ] + }, + { + "cell_type": "markdown", + "id": "cfeb9efc", + "metadata": {}, + "source": [ + "### Load data\n", + "The arm data is stored in [NetCDF](https://en.wikipedia.org/wiki/NetCDF#:~:text=%22NetCDF%20(network%20Common%20Data%20Form,format%20for%20representing%20scientific%20data.) format. We can use xarray's `open_dataset` method to load single file, or `open_mfdataset` to open multiple files. (Note: the latter will still return a single xarray dataset object by combining multiple files.)\n", + "\n", + "Note: open_dataset keeps the file handle open and lazy loads its contents. All parameters are passed directly to open_dataset. It is a preferable method over load_dataset for memory-effiency. (more details, please see [xarray.open_dataset](https://docs.xarray.dev/en/stable/generated/xarray.open_dataset.html))\n", + "\n", + "See the following example in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a440a329", + "metadata": {}, + "outputs": [], + "source": [ + "# open single file\n", + "full_path = latest_file\n", + "print(full_path)\n", + "ds_single = xr.open_dataset(full_path)\n", + "ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0143a3d", + "metadata": {}, + "outputs": [], + "source": [ + "print(ds_single)\n", + "print(type(ds_single))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c0f8939", + "metadata": {}, + "outputs": [], + "source": [ + "# open multiple files\n", + "n_files = 3\n", + "full_paths = [os.path.join(datastream_dir, f_path) for f_path in file_sorted_reverse[:n_files]]\n", + "print(full_paths)\n", + "try: # Note: sometimes multiple files cannot be merged, so we used try except here.\n", + " ds_mutiple = xr.open_mfdataset(full_paths)\n", + " # ds_mutiple\n", + " print(type(ds_mutiple))\n", + " print(ds_mutiple)\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "3fa59943", + "metadata": {}, + "source": [ + "## Explore the data" + ] + }, + { + "cell_type": "markdown", + "id": "571f69b9", + "metadata": {}, + "source": [ + "### NetCDF Data structure \n", + "\n", + "\n", + "(If you are confident with NetCDF basics and xarray essentials, feel free to skip this session.)\n", + "Before we dive into data exploration, there are some eseential concpets we should be familiar with (shown below): \n", + "* Dataset\n", + "* Data array\n", + "* Variable\n", + "* Dimenssion\n", + "* Coordinate\n", + "* Data Type\n", + "* Meta Data (Attributes)\n", + "\n", + "We will not go into details about NetCDF basics and here are some references you might find helpful\n", + "* [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#)\n", + "* [Network Common Data Form (NetCDF)](https://www.unidata.ucar.edu/software/netcdf/) and [A Brief History of (netCDF) Time](https://www.unidata.ucar.edu/software/netcdf/time/recs.html)\n", + "\n", + "\n", + "\n", + "### Xarray essentials\n", + "Earlier we introduced the xarray pacakge and used `open_dataset` and `open_mfdataset` to retrieve NetCDF data file as an xarray Dataset object. Recall that you can review the data in a notebook by using `print(ds)` or `ds`. \n", + "\n", + "Using xarray to retrieve the aforementioned NetCDF basics is straightforwared. In general,\n", + "* Dataset: `ds`\n", + "* Data array: `ds.variables`\n", + "* Variable: `ds.ds.variables`\n", + "* Dimenssion: `ds.dims`\n", + "* Coordinate: `ds.coords`\n", + "* Data Type: `type`\n", + "* Meta Data (Attributes)\n", + "\n", + "Also, here are some references if you are new to xarray\n", + "* [Xarray in 45 minutes](https://tutorial.xarray.dev/overview/xarray-in-45-min.html)\n", + "* [Handling NetCDF Files using XArray for Absolute Beginners](https://towardsdatascience.com/handling-netcdf-files-using-xarray-for-absolute-beginners-111a8ab4463f)\n", + "\n", + "Try the following commands in action\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b3eac323", + "metadata": {}, + "outputs": [], + "source": [ + "ds = ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77ecf85d", + "metadata": {}, + "outputs": [], + "source": [ + "# Dataset \n", + "ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25e7de09", + "metadata": {}, + "outputs": [], + "source": [ + "# Data array, Variable\n", + "# Note: the info can be overwhelming. Do not attempt to grasp everything at the first glance.\n", + "ds.variables" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c41b67e", + "metadata": {}, + "outputs": [], + "source": [ + "# dimenssions\n", + "ds.dims" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "156f1dfc", + "metadata": {}, + "outputs": [], + "source": [ + "# coordinates\n", + "ds.coords" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "277d6064", + "metadata": {}, + "outputs": [], + "source": [ + "# Meta Data (Attributes)\n", + "ds.attrs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d334681f", + "metadata": {}, + "outputs": [], + "source": [ + "# type\n", + "print(type(ds))\n", + "print(type(ds.variables))\n", + "print(type(ds.dims))\n", + "print(type(ds.coords))\n", + "print(type(ds.attrs))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "643399d6", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: retrieve variable names only\n", + "list(ds.variables)" + ] + }, + { + "cell_type": "markdown", + "id": "1cf36878", + "metadata": {}, + "source": [ + "#### Discussion: variable vs. coordinates vs. dimenssions. \n", + "From [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#):\n", + "\n", + "* Dimession: A dimension may be used to represent a real physical dimension, for example, time, latitude, longitude, or height. A dimension might also be used to index other quantities, for example station or model-run-number.\n", + "A netCDF dimension has both a name and a length. A dimension length is an arbitrary positive integer, except that one dimension in a netCDF dataset can have the length UNLIMITED.\n", + "\n", + "* Variables: Variables are used to store the bulk of the data in a netCDF dataset. A variable represents an array of values of the same type. A scalar value is treated as a 0-dimensional array. A variable has a name, a data type, and a shape described by its list of dimensions specified when the variable is created. A variable may also have associated attributes, which may be added, deleted or changed after the variable is created.\n", + "\n", + "* Coordinate (Variables): It is legal for a variable to have the same name as a dimension. Such variables have no special meaning to the netCDF library. However there is a convention that such variables should be treated in a special way by software using this library.\n", + "A variable with the same name as a dimension is called a coordinate variable. It typically defines a physical coordinate corresponding to that dimension.\n", + "\n", + "\n", + "Tips: By definition, coordinates and dimenssions are also variable. For example ds.time is a coordinate but it is also a special variable. But in practice, when we talk about variable, it implies regular/non-coordinate variable. We will use this convention for the remaining notebook. " + ] + }, + { + "cell_type": "markdown", + "id": "d47120f9", + "metadata": {}, + "source": [ + "### Xarray Variable\n", + "\n", + "\n", + "We can use `ds.variables` to access varialbes of a dataset. But many times viewing print-out the whole datasets can be overwhemling. Instead, we would work on the individual variable (also called Xarray data array.) using the `ds[var_name]` syntax, where `var_name` is the name of the variable. See the following examples in action.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5a57e136", + "metadata": {}, + "outputs": [], + "source": [ + "print(type(ds[\"time\"]))\n", + "ds[\"time\"]" + ] + }, + { + "cell_type": "markdown", + "id": "51ec643e", + "metadata": {}, + "source": [ + "#### Variable properties\n", + "For individual varible, the following properties are mostly used with the assocaited xarray method to retrieve them. (assuming var = ds[var_name])\n", + "* name: `var.name`\n", + "* data content: `var.data`\n", + "* attributes: `var.attrs`\n", + "* dimenstions: `var.dims`\n", + "* data type: `var.data.dtype`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a632a525", + "metadata": {}, + "outputs": [], + "source": [ + "var_name = \"time\"\n", + "var = ds[var_name]\n", + "\n", + "print(\"var.name: \\n\", var.name, \"\\n\")\n", + "print(\"var.data: \\n\", var.data, \"\\n\")\n", + "print(\"var.attrs: \\n\", var.attrs, \"\\n\")\n", + "print(\"var.dims: \\n\", var.dims, \"\\n\")\n", + "print(\"var.data.dtype: \\n\", var.data.dtype, \"\\n\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b256b07f", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: indivdual variable info table (collect and display variable info as a tabular format)\n", + "df_info = pd.DataFrame()\n", + "df_info[\"var_name\"] = list(ds.variables)\n", + "df_info[\"dims\"] = df_info.var_name.apply(lambda x: ds[x].dims)\n", + "df_info[\"is_dim\"] = df_info.var_name.apply(lambda x: x in ds.dims)\n", + "df_info[\"n_dim\"] = df_info.var_name.apply(lambda x: len(ds[x].dims))\n", + "df_info[\"attrs\"] = df_info.var_name.apply(lambda x: ds[x].attrs)\n", + "df_info[\"dtype\"] = df_info.var_name.apply(lambda x: ds[x].data.dtype)\n", + "df_info" + ] + }, + { + "cell_type": "markdown", + "id": "e6a36b96", + "metadata": {}, + "source": [ + "### Data cleaning/Preprocessing (skipped)\n", + "Data cleaning and preprocessing is an important stage in the the data analysis pipeline. However it will be out of the scope of this notebook. For more information about data cleaning and preprocessing basics, here are some references. \n", + "* [Xarray Fundamentals](https://earth-env-data-science.github.io/lectures/xarray/xarray.html)\n", + "* [Xarray Tutorial — Pangeo Gallery documentation](http://gallery.pangeo.io/repos/pangeo-data/pangeo-tutorial-gallery/xarray.html)\n", + "* [Pythonic Data Cleaning With pandas and NumPy](https://realpython.com/python-data-cleaning-numpy-pandas/)\n", + "* [Pandas - Cleaning Data](https://www.w3schools.com/python/pandas/pandas_cleaning.asp)" + ] + }, + { + "cell_type": "markdown", + "id": "ae6e1a69", + "metadata": {}, + "source": [ + "### Plotting\n", + "Data visualization (or plotting) is another important data analysis topic and deserves its own discussion. Here in this notebook we will only demonstrate basic tool for simple data visualization tasks.\n", + "\n", + "Here are some reference you might find useful:\n", + "* xarray plotting: https://docs.xarray.dev/en/stable/user-guide/plotting.html\n", + "* Atmospheric Community Toolkit (ACT): https://arm-doe.github.io/ACT/index.html\n", + "\n", + "Note: this notebook is auto-generated using a template. It uses a general idea to select variable(s) to plot and is not customized to each indivdual notebook. Feel free to change the variables in intrrests, especially certain figure is failed to plot." + ] + }, + { + "cell_type": "markdown", + "id": "7e417fb2", + "metadata": {}, + "source": [ + "#### 1-dimenssional basic time series plot\n", + "\n", + "For the following plot we would like to find variables such that\n", + "* it has one and only one dimession\n", + "* \"time\" is its coordinate variable\n", + "* it is not a dimenssion itself,\n", + "* it is not a special variable with substrings within [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "\n", + "Please see the following example in action. (For more details about pandas filtering, please see the following references.)\n", + "* [pandas: multiple conditions while indexing data frame](https://stackoverflow.com/questions/22591174/pandas-multiple-conditions-while-indexing-data-frame-unexpected-behavior)\n", + "* [pandas.DataFrame.apply](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.apply.html)\n", + "* [How to test if a string contains one of the substrings in a list...](https://stackoverflow.com/questions/26577516/how-to-test-if-a-string-contains-one-of-the-substrings-in-a-list-in-pandas" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30edac2d", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==1, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter = df_info[(df_info.n_dim == 1) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4777a659", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_1d = df_filter.var_name.values[0]\n", + " var_1d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb5ae985", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " ds[var_1d].plot()\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "9e41066e", + "metadata": {}, + "source": [ + "#### 2-dimenssional basic plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09bd4adc", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==2, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_2 = df_info[(df_info.n_dim == 2) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2cdea1a9", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_2d = df_filter_2.var_name.values[0]\n", + " var_2d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5f48879", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " print(ds[var_2d].dims)\n", + " # ds[var_2d].plot()\n", + "\n", + " # conventionally, use \"time\" as x-axis\n", + " ds[var_2d].plot(x=\"time\")\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "7554b2a6", + "metadata": {}, + "source": [ + "#### qc-plotting (optional)\n", + "\n", + "Note: act qc plotting has more strict requirement, one of them is the associated qc variable need to have \"flag_masks\" attributes. Which is added by using ds = act.io.armfiles.read_netcdf(files_list), then ds.clean.cleanup()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71c2096f", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " ds_act = act.io.armfiles.read_netcdf(full_path)\n", + " print(type(ds_act))\n", + " ds_act.clean.cleanup()\n", + "\n", + " # or \n", + " # ds.clean.cleanup()\n", + "except Exception as e:\n", + " print(\"ERROR\", e)\n", + " ds_act = ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "844f8505", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter valid variables for ACT qc plotting\n", + "condition = (\"qc_\" + df_info.var_name).apply(lambda x: ds_act[x].attrs.get(\"flag_masks\") is not None \n", + " if x in list(ds.variables) else False)\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_3 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings))) &\n", + " condition\n", + " ]\n", + "df_filter_3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd38cc6", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "try:\n", + " qc_variable = df_filter_3.var_name.values[0]\n", + " print(qc_variable)\n", + "except Exception as e:\n", + " print(e)\n", + " \n", + "try:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + " qc_ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "d76e4d27", + "metadata": {}, + "source": [ + "#### bonus: choose variables to plot from a dropdown menu " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb733804", + "metadata": {}, + "outputs": [], + "source": [ + "# Valid variables filtering\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_4 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82d5c20d", + "metadata": {}, + "outputs": [], + "source": [ + "# example 1: using xarray plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2fc9aaa4", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# fig, ax = plt.subplots(figsize=(10, 4))\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "# fig.clear() # Remove old lines from plot and plot new one\n", + "# if len(ds[var].dims)==2:\n", + "# ds[var].plot(x=\"time\", add_colorbar=False)\n", + "# else:\n", + "# ds[var].plot()\n", + "# plt.grid()\n", + "# plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fabdd802", + "metadata": {}, + "outputs": [], + "source": [ + "# example 2: using act plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "962b4186", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "\n", + "\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "\n", + "# i_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + "# i_display.add_subplots((1,), figsize=(10, 4))\n", + "# ax = i_display.plot(var, subplot_index=(0,), set_title=f\"{var} ({ds_act[var].attrs['long_name']})\",)\n", + "\n", + "# ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "# ax.grid()\n", + "# plt.show()\n" + ] + }, + { + "cell_type": "markdown", + "id": "237bfd37", + "metadata": {}, + "source": [ + "## Point Cloud of Cloud of Points" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1d0eb162", + "metadata": {}, + "outputs": [], + "source": [ + "# this variable represents the index of the data point that is being shown\n", + "print(f\"Available time values: {ds.time.dt.strftime(r'%Y-%m-%d %H:%M:%S').values[0]} -- {ds.time.dt.strftime(r'%Y-%m-%d %H:%M:%S').values[-1]}\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "34ab9eb0", + "metadata": {}, + "outputs": [], + "source": [ + "# Enter timestamp to plot (format: YYYY-MM-DD hh:mm:ss)\n", + "display_time = '2020-03-02 15:00:00'\n", + "\n", + "# list available time stamps\n", + "display_dt = datetime.strptime(display_time, r'%Y-%m-%d %H:%M:%S')\n", + "available_times = np.array([datetime.combine(d,t) for d, t in zip(ds.time.dt.date.values,ds.time.dt.time.values)])\n", + "# get closest time \n", + "time_index = np.argmin(np.abs(available_times - display_dt))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ad6a8741", + "metadata": {}, + "outputs": [], + "source": [ + "x_relative_var = ds.variables['x_relative'][time_index]\n", + "y_relative_var = ds.variables['y_relative'][time_index]\n", + "z_relative_var = ds.variables['z_relative'][time_index]\n", + "\n", + "# # Filter out values that exceed 50 km\n", + "ind_nonzero = tuple(np.nonzero((np.abs(x_relative_var) < 50000)))\n", + "print(len(x_relative_var[0]),' cloud points are extracted')\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f84281f5", + "metadata": {}, + "outputs": [], + "source": [ + "x_slice = np.array(x_relative_var[ind_nonzero])/1000 # convert to km \n", + "y_slice = np.array(y_relative_var[ind_nonzero])/1000\n", + "z_slice = np.array(z_relative_var[ind_nonzero])/1000\n", + "\n", + "###2D plot of x,y variables in subplot(2,2,si)\n", + "def plot2D(x,y,fig,si,xlabel,ylabel):\n", + " ax = fig.add_subplot(2,2,si)\n", + " ax.scatter(x, y, s=4, marker='o', c= 'gray')\n", + " ax.set_xlabel(xlabel)\n", + " ax.set_ylabel(ylabel)\n", + " ax.xaxis.labelpad = 5\n", + " ax.yaxis.labelpad = 5\n", + "\n", + "###3D plot of x,y,z variables in subplot(2,2,si)\n", + "def plot3D(x,y,z,fig,si):\n", + " #check if data point count is sufficient for display\n", + " if (len(x)>10):\n", + " ax = fig.add_subplot(1,1,si, projection='3d')\n", + " x = [x[0:len(x)]]\n", + " y = [y[0:len(y)]]\n", + " z = [z[0:len(z)]]\n", + " x1 = int(min(min(x)))\n", + " x2 = int(max(max(x)))\n", + " y1 = int(min(min(y)))\n", + " y2 = int(max(max(y)))\n", + " z2 = int(max(max(z)))\n", + " \n", + " ax.scatter(x, y, z, c='gray', marker='o')\n", + " # ax.xaxis.set_ticks(np.arange(x1,x2,int((x2-x1+2)/4)+0.5))\n", + " # ax.yaxis.set_ticks(np.arange(y1,y2,int((y2-y1+2)/4)+0.5))\n", + " ax.view_init(elev=15, azim=-70)\n", + " ax.set_xlabel('X [km] ')\n", + " ax.set_ylabel('Y [km] ')\n", + " ax.xaxis.labelpad = 15\n", + " ax.yaxis.labelpad = 15\n", + " ax.zaxis.set_ticks(np.arange(0,int(z2+1),.5))\n", + " ax.set_zlabel('Z [km] ')\n", + " \n", + "fig = plt.figure(figsize=(9.5,10))\n", + "plot2D(x_slice,z_slice,fig,1,'direction eastward [km]','altitude above the ground [km]')\n", + "plot2D(y_slice,z_slice,fig,2,'direction northward [km]','altitude above the ground [km]')\n", + "plot2D(x_slice,y_slice,fig,3,'direction eastward [km]','direction northward [km]')\n", + "fig = plt.figure(figsize=(9.5,10))\n", + "plot3D(x_slice,y_slice,z_slice,fig,1)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/PCCP/pccp.c1.ipynb b/VAPs/quicklook/PCCP/pccp.c1.ipynb new file mode 100644 index 00000000..0b3b934a --- /dev/null +++ b/VAPs/quicklook/PCCP/pccp.c1.ipynb @@ -0,0 +1,310 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# PCCP.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/pccp) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'pccp'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2022-09-26', 'facility': 'S5', 'site': 'hou', 'start_date': '2021-09-17'}, {'end_date': '2020-03-03', 'facility': 'E43', 'site': 'sgp', 'start_date': '2017-09-01'}, {'end_date': '2019-12-01', 'facility': 'E44', 'site': 'sgp', 'start_date': '2017-09-01'}, {'end_date': '2019-10-30', 'facility': 'E45', 'site': 'sgp', 'start_date': '2017-09-01'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'E43' )\n", + "\n", + "date_start = '2020-03-02'\n", + "date_end = '2020-03-03'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "cell_type": "markdown", + "id": "822caf5f", + "metadata": {}, + "source": [ + "## Point Cloud of Cloud of Points" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1fe5cb65", + "metadata": {}, + "outputs": [], + "source": [ + "# this variable represents the index of the data point that is being shown\n", + "print(f\"Available time values: {ds.time.dt.strftime(r'%Y-%m-%d %H:%M:%S').values[0]} -- {ds.time.dt.strftime(r'%Y-%m-%d %H:%M:%S').values[-1]}\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b1c97574", + "metadata": {}, + "outputs": [], + "source": [ + "# Enter timestamp to plot (format: YYYY-MM-DD hh:mm:ss)\n", + "display_time = '2020-03-02 15:00:00'\n", + "\n", + "# list available time stamps\n", + "display_dt = datetime.strptime(display_time, r'%Y-%m-%d %H:%M:%S')\n", + "available_times = np.array([datetime.combine(d,t) for d, t in zip(ds.time.dt.date.values,ds.time.dt.time.values)])\n", + "# get closest time \n", + "time_index = np.argmin(np.abs(available_times - display_dt))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "dc2401b0", + "metadata": {}, + "outputs": [], + "source": [ + "x_relative_var = ds.variables['x_relative'][time_index]\n", + "y_relative_var = ds.variables['y_relative'][time_index]\n", + "z_relative_var = ds.variables['z_relative'][time_index]\n", + "\n", + "# # Filter out values that exceed 50 km\n", + "ind_nonzero = tuple(np.nonzero((np.abs(x_relative_var) < 50000)))\n", + "print(len(x_relative_var[0]),' cloud points are extracted')\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c0587f1", + "metadata": {}, + "outputs": [], + "source": [ + "x_slice = np.array(x_relative_var[ind_nonzero])/1000 # convert to km \n", + "y_slice = np.array(y_relative_var[ind_nonzero])/1000\n", + "z_slice = np.array(z_relative_var[ind_nonzero])/1000\n", + "\n", + "###2D plot of x,y variables in subplot(2,2,si)\n", + "def plot2D(x,y,fig,si,xlabel,ylabel):\n", + " ax = fig.add_subplot(2,2,si)\n", + " ax.scatter(x, y, s=4, marker='o', c= 'gray')\n", + " ax.set_xlabel(xlabel)\n", + " ax.set_ylabel(ylabel)\n", + " ax.xaxis.labelpad = 5\n", + " ax.yaxis.labelpad = 5\n", + "\n", + "###3D plot of x,y,z variables in subplot(2,2,si)\n", + "def plot3D(x,y,z,fig,si):\n", + " #check if data point count is sufficient for display\n", + " if (len(x)>10):\n", + " ax = fig.add_subplot(1,1,si, projection='3d')\n", + " x = [x[0:len(x)]]\n", + " y = [y[0:len(y)]]\n", + " z = [z[0:len(z)]]\n", + " x1 = int(min(min(x)))\n", + " x2 = int(max(max(x)))\n", + " y1 = int(min(min(y)))\n", + " y2 = int(max(max(y)))\n", + " z2 = int(max(max(z)))\n", + " \n", + " ax.scatter(x, y, z, c='gray', marker='o')\n", + " # ax.xaxis.set_ticks(np.arange(x1,x2,int((x2-x1+2)/4)+0.5))\n", + " # ax.yaxis.set_ticks(np.arange(y1,y2,int((y2-y1+2)/4)+0.5))\n", + " ax.view_init(elev=15, azim=-70)\n", + " ax.set_xlabel('X [km] ')\n", + " ax.set_ylabel('Y [km] ')\n", + " ax.xaxis.labelpad = 15\n", + " ax.yaxis.labelpad = 15\n", + " ax.zaxis.set_ticks(np.arange(0,int(z2+1),.5))\n", + " ax.set_zlabel('Z [km] ')\n", + " \n", + "fig = plt.figure(figsize=(9.5,10))\n", + "plot2D(x_slice,z_slice,fig,1,'direction eastward [km]','altitude above the ground [km]')\n", + "plot2D(y_slice,z_slice,fig,2,'direction northward [km]','altitude above the ground [km]')\n", + "plot2D(x_slice,y_slice,fig,3,'direction eastward [km]','direction northward [km]')\n", + "fig = plt.figure(figsize=(9.5,10))\n", + "plot3D(x_slice,y_slice,z_slice,fig,1)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/PSAP/.ipynb_checkpoints/aospsap3w.c1-checkpoint.ipynb b/VAPs/quicklook/PSAP/.ipynb_checkpoints/aospsap3w.c1-checkpoint.ipynb new file mode 100644 index 00000000..73feaecb --- /dev/null +++ b/VAPs/quicklook/PSAP/.ipynb_checkpoints/aospsap3w.c1-checkpoint.ipynb @@ -0,0 +1,1841 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# AOSPSAP3W.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/psap) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'aospsap3w'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2013-06-24', 'facility': 'M1', 'site': 'pvc', 'start_date': '2012-07-16'}]" + ] + }, + { + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "ac6764f5", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The following locations and date ranges are available for this VAP:\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
sitefacilitystart_dateend_date
0pvcM12012-07-162013-06-24
\n", + "
" + ], + "text/plain": [ + " site facility start_date end_date\n", + "0 pvc M1 2012-07-16 2013-06-24" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'pvc', 'M1' )\n", + "\n", + "date_start = '2013-06-22'\n", + "date_end = '2013-06-24'" + ] + }, + { + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'/data/archive/pvc/pvcaospsap3wM1.c1'" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['20130622', '20130623', '20130624']" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "51feea2e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['/data/archive/pvc/pvcaospsap3wM1.c1/pvcaospsap3wM1.c1.20130622.000000.cdf',\n", + " '/data/archive/pvc/pvcaospsap3wM1.c1/pvcaospsap3wM1.c1.20130623.000000.cdf',\n", + " '/data/archive/pvc/pvcaospsap3wM1.c1/pvcaospsap3wM1.c1.20130624.000000.cdf']" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "3 files loaded\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
<xarray.Dataset>\n",
+       "Dimensions:              (time: 3923)\n",
+       "Coordinates:\n",
+       "  * time                 (time) datetime64[ns] 2013-06-22 ... 2013-06-24T18:0...\n",
+       "Data variables: (12/21)\n",
+       "    base_time            (time) datetime64[ns] 2013-06-22 ... 2013-06-24\n",
+       "    time_offset          (time) datetime64[ns] 2013-06-22 ... 2013-06-24T18:0...\n",
+       "    Ba_B_PSAP3W          (time) float32 dask.array<chunksize=(1440,), meta=np.ndarray>\n",
+       "    qc_Ba_B_PSAP3W       (time) int32 dask.array<chunksize=(1440,), meta=np.ndarray>\n",
+       "    Ba_G_PSAP3W          (time) float32 dask.array<chunksize=(1440,), meta=np.ndarray>\n",
+       "    qc_Ba_G_PSAP3W       (time) int32 dask.array<chunksize=(1440,), meta=np.ndarray>\n",
+       "    ...                   ...\n",
+       "    qc_sample_length     (time) int32 dask.array<chunksize=(1440,), meta=np.ndarray>\n",
+       "    impactor_setting     (time) float32 dask.array<chunksize=(1440,), meta=np.ndarray>\n",
+       "    qc_impactor_setting  (time) int32 dask.array<chunksize=(1440,), meta=np.ndarray>\n",
+       "    lat                  (time) float32 42.03 42.03 42.03 ... 42.03 42.03 42.03\n",
+       "    lon                  (time) float32 -70.05 -70.05 -70.05 ... -70.05 -70.05\n",
+       "    alt                  (time) float32 43.0 43.0 43.0 43.0 ... 43.0 43.0 43.0\n",
+       "Attributes: (12/21)\n",
+       "    command_line:             aosmqc_ingest -s pvc -f M1 -n aosmqc -R -D\n",
+       "    process_version:          ingest-aosmqc-1.2-0.el6\n",
+       "    dod_version:              aospsap3w-c1-1.3\n",
+       "    site_id:                  pvc\n",
+       "    facility_id:              M1: Cape Cod, Massachusetts\n",
+       "    data_level:               c1\n",
+       "    ...                       ...\n",
+       "    datastream:               pvcaospsap3wM1.c1\n",
+       "    history:                  created by user dsmgr on machine tin at 2014-06...\n",
+       "    _file_dates:              ['20130622', '20130623', '20130624']\n",
+       "    _file_times:              ['000000', '000000', '000000']\n",
+       "    _datastream:              pvcaospsap3wM1.c1\n",
+       "    _arm_standards_flag:      1
" + ], + "text/plain": [ + "\n", + "Dimensions: (time: 3923)\n", + "Coordinates:\n", + " * time (time) datetime64[ns] 2013-06-22 ... 2013-06-24T18:0...\n", + "Data variables: (12/21)\n", + " base_time (time) datetime64[ns] 2013-06-22 ... 2013-06-24\n", + " time_offset (time) datetime64[ns] 2013-06-22 ... 2013-06-24T18:0...\n", + " Ba_B_PSAP3W (time) float32 dask.array\n", + " qc_Ba_B_PSAP3W (time) int32 dask.array\n", + " Ba_G_PSAP3W (time) float32 dask.array\n", + " qc_Ba_G_PSAP3W (time) int32 dask.array\n", + " ... ...\n", + " qc_sample_length (time) int32 dask.array\n", + " impactor_setting (time) float32 dask.array\n", + " qc_impactor_setting (time) int32 dask.array\n", + " lat (time) float32 42.03 42.03 42.03 ... 42.03 42.03 42.03\n", + " lon (time) float32 -70.05 -70.05 -70.05 ... -70.05 -70.05\n", + " alt (time) float32 43.0 43.0 43.0 43.0 ... 43.0 43.0 43.0\n", + "Attributes: (12/21)\n", + " command_line: aosmqc_ingest -s pvc -f M1 -n aosmqc -R -D\n", + " process_version: ingest-aosmqc-1.2-0.el6\n", + " dod_version: aospsap3w-c1-1.3\n", + " site_id: pvc\n", + " facility_id: M1: Cape Cod, Massachusetts\n", + " data_level: c1\n", + " ... ...\n", + " datastream: pvcaospsap3wM1.c1\n", + " history: created by user dsmgr on machine tin at 2014-06...\n", + " _file_dates: ['20130622', '20130623', '20130624']\n", + " _file_times: ['000000', '000000', '000000']\n", + " _datastream: pvcaospsap3wM1.c1\n", + " _arm_standards_flag: 1" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['transmittance_blue', 'dqrvar_transmittance_blue', 'transmittance_green']" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [ + { + "ename": "KeyError", + "evalue": "'transmittance_blue'", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mKeyError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[0;32mIn[9], line 5\u001b[0m\n\u001b[1;32m 2\u001b[0m ts_display\u001b[38;5;241m.\u001b[39madd_subplots((\u001b[38;5;28mlen\u001b[39m(variables_to_plot),), figsize \u001b[38;5;241m=\u001b[39m (\u001b[38;5;241m9.5\u001b[39m,\u001b[38;5;241m4\u001b[39m\u001b[38;5;241m*\u001b[39m\u001b[38;5;28mlen\u001b[39m(variables_to_plot)))\n\u001b[1;32m 4\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m i,v \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28menumerate\u001b[39m(variables_to_plot):\n\u001b[0;32m----> 5\u001b[0m ts_ax \u001b[38;5;241m=\u001b[39m ts_display\u001b[38;5;241m.\u001b[39mplot(v, subplot_index\u001b[38;5;241m=\u001b[39m(i,), set_title\u001b[38;5;241m=\u001b[39m\u001b[43mds\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mvariables\u001b[49m\u001b[43m[\u001b[49m\u001b[43mv\u001b[49m\u001b[43m]\u001b[49m\u001b[38;5;241m.\u001b[39mattrs[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mlong_name\u001b[39m\u001b[38;5;124m'\u001b[39m],)\n\u001b[1;32m 6\u001b[0m ts_ax\u001b[38;5;241m.\u001b[39mgrid()\n\u001b[1;32m 8\u001b[0m plt\u001b[38;5;241m.\u001b[39mshow()\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/core/utils.py:453\u001b[0m, in \u001b[0;36mFrozen.__getitem__\u001b[0;34m(self, key)\u001b[0m\n\u001b[1;32m 452\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21m__getitem__\u001b[39m(\u001b[38;5;28mself\u001b[39m, key: K) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m V:\n\u001b[0;32m--> 453\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mmapping\u001b[49m\u001b[43m[\u001b[49m\u001b[43mkey\u001b[49m\u001b[43m]\u001b[49m\n", + "\u001b[0;31mKeyError\u001b[0m: 'transmittance_blue'" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "f164bc0d8b484beaa248597fc1245960", + "version_major": 2, + "version_minor": 0 + }, + "image/png": "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1tnZmUVE9thjj2WdnZ3ZW2+9lWWZ6+/JJmwT9uSTT2aVlZVZYWFhNn/+/Gznzp3D/+zuu+/ObrrpphHH/+d//mf2J3/yJ1lhYWH2uc99Ltu8eXOOJ2aqGs9euummm7KIGPW4++67cz84U854f1/6XcKW3zXevXTw4MHs1ltvzS677LJs9uzZWXNzc/bBBx/keGqmqvHup8cffzz74he/mF122WVZeXl59ld/9VfZ0aNHczw1U81//Md/fOw1kOvvyZWXZd5TAQAAQLp8xhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAnbCbZr165YtmxZzJo1K/Ly8uKVV175xHN27twZ1dXVUVxcHNddd1089dRTEz8oAABAooTtBHv//fdj3rx58cQTT5zT8YcPH46lS5dGXV1ddHZ2xkMPPRSrVq2KF198cYInBQAASFNelmXZZA9xqcjLy4uXX345li9fftZjvv3tb8err74aBw8eHF5ramqKn/3sZ7Fv374cTAkAAJCWgskegJH27dsX9fX1I9Zuv/322LJlS3z44Ycxffr0Mc8bGhqKoaGh4eenT5+Od999N2bMmBF5eXkTOjMAAFzqsiyL48ePx6xZs2LaNG+MzTVhO8X09vZGWVnZiLWysrI4efJk9PX1RXl5+ZjntbS0xIYNG3IxIgAAcBZHjhyJ2bNnT/YYlxxhOwX9/h3WM+8W/7g7r+vWrYvm5ubh5wMDA3HttdfGkSNHoqSkZGIGBQAAIiJicHAwKioq4g//8A8ne5RLkrCdYq6++uro7e0dsXbs2LEoKCiIGTNmnPW8oqKiKCoqGrVeUlIibAEAIEd8DHByePP3FLNo0aJob28fsbZjx46oqak56+drAQAALmXCdoK999570dXVFV1dXRHx0df5dHV1RXd3d0R89BbixsbG4eObmprirbfeiubm5jh48GBs3bo1tmzZEg888MBkjA8AADDleSvyBNu/f3/cfPPNw8/P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7dixY1FQUBAzZsw463lFRUVRVFQ0ar2kpETYAgBAjvgY4OTw5u8pZtGiRdHe3j5ibceOHVFTU3PWz9cCAABcyoTtBHvvvfeiq6srurq6IuKjr/Pp6uqK7u7uiPjoLcSNjY3Dxzc1NcVbb70Vzc3NcfDgwdi6dWts2bIlHnjggckYHwAAYMrzVuQJtn///rj55puHn5/5HOzdd98d27Zti56enuHIjYioqqqKtra2WLNmTTz55JMxa9asePzxx+NrX/tazmcHAABIge+xvUgNDg5GaWlpDAwM+IwtAABMMNffk8tbkQEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAnbHNm0aVNUVVVFcXFxVFdXx+7duz/2+O3bt8e8efPi8ssvj/Ly8rj33nujv78/R9MCAACkQ9jmQGtra6xevTrWr18fnZ2dUVdXF0uWLInu7u4xj9+zZ080NjbGypUr44033ojnn38+fvrTn8Z9992X48kBAACmPmGbA4899lisXLky7rvvvpgzZ0780z/9U1RUVMTmzZvHPP6//uu/4nOf+1ysWrUqqqqq4k//9E/jG9/4Ruzfvz/HkwMAAEx9wnaCnThxIjo6OqK+vn7Een19fezdu3fMc2pra+Po0aPR1tYWWZbFO++8Ey+88ELccccduRgZAAAgKcJ2gvX19cWpU6eirKxsxHpZWVn09vaOeU5tbW1s3749GhoaorCwMK6++ur4zGc+E9///vfP+nOGhoZicHBwxAMAAOBSIGxzJC8vb8TzLMtGrZ1x4MCBWLVqVTz88MPR0dERr732Whw+fDiamprO+uu3tLREaWnp8KOiouKCzg8AADBV5WVZlk32EBezEydOxOWXXx7PP/98/MVf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yZlq3n///Zg3b1488cQT53S86+8cy0jSggULsqamphFrN954Y7Z27doxj/+7v/u77MYbbxyx9o1vfCP7yle+MmEzkobx7qWxfPGLX8w2bNhwoUcjMee7lxoaGrK///u/zx555JFs3rx5EzghqRjvXvq3f/u3rLS0NOvv78/FeCRmvPvpH//xH7PrrrtuxNrjjz+ezZ49e8JmJD0Rkb388ssfe4zr79xyxzZBJ06ciI6Ojqivrx+xXl9fH3v37h3znH379o06/vbbb4/9+/fHhx9+OGGzMrWdz176fadPn47jx4/HlVdeOREjkojz3UvPPPNMvPnmm/HII49M9Igk4nz20quvvho1NTXx3e9+N6655pq44YYb4oEHHojf/OY3uRiZKex89lNtbW0cPXo02traIsuyeOedd+KFF16IO+64IxcjcxFx/Z1bBZM9AOPX19cXp06dirKyshHrZWVl0dvbO+Y5vb29Yx5/8uTJ6Ovri/Ly8gmbl6nrfPbS7/ve974X77//fqxYsWIiRiQR57OXfvnLX8batWtj9+7dUVDgP0d85Hz20qFDh2LPnj1RXFwcL7/8cvT19cX9998f7777rs/ZXuLOZz/V1tbG9u3bo6GhIX7729/GyZMn48/+7M/i+9//fi5G5iLi+ju33LFNWF5e3ojnWZaNWvuk48da59Iz3r10xnPPPRff+c53orW1Na666qqJGo+EnOteOnXqVNx5552xYcOGuOGGG3I1HgkZz+9Lp0+fjry8vNi+fXssWLAgli5dGo899lhs27bNXVsiYnz76cCBA7Fq1ap4+OGHo6OjI1577bU4fPhwNDU15WJULjKuv3PHH5EnaObMmZGfnz/qTxqPHTs26k+Fzrj66qvHPL6goCBmzJgxYbMytZ3PXjqjtbU1Vq5cGc8//3zceuutEzkmCRjvXjp+/Hjs378/Ojs741vf+lZEfBQnWZZFQUFB7NixI2655ZaczM7Ucj6/L5WXl8c111wTpaWlw2tz5syJLMvi6NGjcf3110/ozExd57OfWlpaYvHixfHggw9GRMSXvvSluOKKK6Kuri4effRRd9k4Z66/c8sd2wQVFhZGdXV1tLe3j1hvb2+P2traMc9ZtGjRqON37NgRNTU1MX369AmblantfPZSxEd3au+555549tlnfeaIiBj/XiopKYmf//zn0dXVNfxoamqKL3zhC9HV1RULFy7M1ehMMefz+9LixYvj7bffjvfee2947Re/+EVMmzYtZs+ePaHzMrWdz3764IMPYtq0kZfI+fn5EfF/d9vgXLj+zrFJ+kur+JR+/OMfZ9OnT8+2bNmSHThwIFu9enV2xRVXZP/7v/+bZVmWrV27NrvrrruGjz906FB2+eWXZ2vWrMkOHDiQbdmyJZs+fXr2wgsvTNZLYIoY71569tlns4KCguzJJ5/Menp6hh+//vWvJ+slMEWMdy/9Pn8rMmeMdy8dP348mz17dvaXf/mX2RtvvJHt3Lkzu/7667P77rtvsl4CU8h499MzzzyTFRQUZJs2bcrefPPNbM+ePVlNTU22YMGCyXoJTBHHjx/POjs7s87Oziwissceeyzr7OzM3nrrrSzLXH9PNmGbsCeffDKrrKzMCgsLs/nz52c7d+4c/md33313dtNNN404/j//8z+zP/mTP8kKCwuzz33uc9nmzZtzPDFT1Xj20k033ZRFxKjH3XffnfvBmXLG+/vS7xK2/K7x7qWDBw9mt956a3bZZZdls2fPzpqbm7MPPvggx1MzVY13Pz3++OPZF7/4xeyyyy7LysvLs7/6q7/Kjh49muOpmWr+4z/+42OvgVx/T668LPOeCgAAANLlM7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQtP8PDcrXAZbhxkUAAAAASUVORK5CYII=", + "text/html": [ + "\n", + "
\n", + "
\n", + " Figure\n", + "
\n", + " \n", + "
\n", + " " + ], + "text/plain": [ + "Canvas(toolbar=Toolbar(toolitems=[('Home', 'Reset original view', 'home', 'home'), ('Back', 'Back to previous …" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'transmittance_blue'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'transmittance_blue'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/PSAP/PSAP_tutorial.ipynb b/VAPs/quicklook/PSAP/PSAP_tutorial.ipynb new file mode 100644 index 00000000..3eb3aa7b --- /dev/null +++ b/VAPs/quicklook/PSAP/PSAP_tutorial.ipynb @@ -0,0 +1,838 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# AOSPSAP3W.C1 Notebook\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/psap) for more information about this vap." + ] + }, + { + "cell_type": "markdown", + "id": "97097763", + "metadata": {}, + "source": [ + "In this notebook, we demonstrate the workflow to explore ARM vap data (using aospsap3w as an example.) Value-added products (VAPs) are higher-order data products that have been analyzed and processed to ease scientists’ use of ARM data in atmospheric research and global climate models. \n", + "Here is the main content we will cover." + ] + }, + { + "cell_type": "markdown", + "id": "eddec40f", + "metadata": {}, + "source": [ + "# Table of Content\n", + "## Access the data\n", + "* How to retrieve the data\n", + "* Data path and file name conventions\n", + "* Load data\n", + "## Explore the data\n", + "* NetCDF Data structure\n", + "* Xarray essentials\n", + "* Xarray Variable\n", + "## Plot the data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "import random\n", + "\n", + "import glob\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "2372d5be", + "metadata": {}, + "source": [ + "## Access the data" + ] + }, + { + "cell_type": "markdown", + "id": "5b0d9684", + "metadata": {}, + "source": [ + "### How to retrieve the data\n", + "We assume the path \"/data/archive\" is available where you are running this notebook. You can use `os.path.exists(\"/data/archive\")` to verify if the path exists at your machine.\n", + "\n", + "\n", + "### Data path and file name conventions\n", + "There several common terminologies regarding ARM data, for example, data-stream-name, data-level, site, facility, instrument, etc. (For more details, please see [ARM Data File Standards Version 1.2](https://www.google.com/search?q=arm+datastream+facility+cite+definition&rlz=1C1GCEJ_enUS1029US1029&ei=hb41ZICrBPukqtsPvMODIA&ved=0ahUKEwjAgeiS0aL-AhV7kmoFHbzhAAQQ4dUDCBA&uact=5&oq=arm+datastream+facility+cite+definition&gs_lcp=Cgxnd3Mtd2l6LXNlcnAQAzIFCAAQogQyBQgAEKIEMgUIABCiBDIFCAAQogQyBQgAEKIEOgoIABBHENYEELADOgoIIRCgARDDBBAKSgQIQRgAUJ4DWIoOYO0PaAFwAXgAgAGMAYgBuQmSAQMyLjmYAQCgAQHIAQjAAQE&sclient=gws-wiz-serp).) \n", + "\n", + "For example, this notebook is called `aospsap3w.c1`, where `aospsap3w` is the \"datastream name\", and the `{process.ds_class_level}}` is the \"data level\".\n", + "\n", + "This datastream also contains site `pvc` and facility `M1`. (Note: individual datastream might have multiple site-facility pairs.)\n", + "In such a case, the data of this data-stream is stored at `/data/archive/pvc/pvcaospsap3wM1.c1`, which is in the format of `//.`. We can use the following method to assign the data-stream directory `datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )`\n", + "\n", + "The data files under datastream_dir also follows naming conventions. But once reach the datastream_dir level, the most import file naming convention to differentiate the files is \"yyyyMMdd.hhmmss\", which comes handy to filter out files based on datetime. For example, we can use `glob.glob(f'{datastream_dir}/*.200709*.*')` to filter files in 2007 September.\n", + "\n", + "(Note: refer to the https://adc.arm.gov/discovery/#/ and https://adc.arm.gov/solr8/metadata/select API to explore ARM datastream and assoicated available sites and facilities\n", + "\n", + "Please see the following examples in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d7e9eb85", + "metadata": {}, + "outputs": [], + "source": [ + "# Verify if DATA_DIR path exists\n", + "DATA_DIR = \"/data/archive\"\n", + "os.path.exists(DATA_DIR)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "586993fd", + "metadata": {}, + "outputs": [], + "source": [ + "# Speicify datastream_dir following the path conventions and check its existence\n", + "DATASTREAM_NAME = \"aospsap3w\"\n", + "DATA_LEVEL = \"c1\"\n", + "site = \"pvc\"\n", + "facility = \"M1\"\n", + "datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )\n", + "print(datastream_dir)\n", + "print(os.path.exists(datastream_dir))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0742f7c1", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: list 5 (random) files under datastream_dir\n", + "files = os.listdir(datastream_dir)\n", + "files[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39b98a36", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: get most recent file\n", + "list_of_files = glob.glob(f\"{datastream_dir}/*\") # * means all if need specific format then *.csv\n", + "latest_file = max(list_of_files, key=os.path.getctime)\n", + "latest_file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "902d514e", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus sort datastream files based on datetime\n", + "files = os.listdir(datastream_dir)\n", + "file_sorted = files.copy()\n", + "file_sorted.sort() \n", + "print(file_sorted[:5])\n", + "\n", + "# to reverse\n", + "file_sorted_reverse = files.copy() \n", + "file_sorted_reverse.sort(reverse=True)\n", + "print(file_sorted_reverse[:5])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec5923b2", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: pattern matching\n", + "# filter the 200709** files under datastream_dir\n", + "files_filter = glob.glob(f'{datastream_dir}/*.200709*.*')\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c4a4aa18", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: use armnotebook_utils.file_filter (TODO) to filter files based on datastream info \n" + ] + }, + { + "cell_type": "markdown", + "id": "cfeb9efc", + "metadata": {}, + "source": [ + "### Load data\n", + "The arm data is stored in [NetCDF](https://en.wikipedia.org/wiki/NetCDF#:~:text=%22NetCDF%20(network%20Common%20Data%20Form,format%20for%20representing%20scientific%20data.) format. We can use xarray's `open_dataset` method to load single file, or `open_mfdataset` to open multiple files. (Note: the latter will still return a single xarray dataset object by combining multiple files.)\n", + "\n", + "Note: open_dataset keeps the file handle open and lazy loads its contents. All parameters are passed directly to open_dataset. It is a preferable method over load_dataset for memory-effiency. (more details, please see [xarray.open_dataset](https://docs.xarray.dev/en/stable/generated/xarray.open_dataset.html))\n", + "\n", + "See the following example in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a440a329", + "metadata": {}, + "outputs": [], + "source": [ + "# open single file\n", + "full_path = latest_file\n", + "print(full_path)\n", + "ds_single = xr.open_dataset(full_path)\n", + "ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0143a3d", + "metadata": {}, + "outputs": [], + "source": [ + "print(ds_single)\n", + "print(type(ds_single))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c0f8939", + "metadata": {}, + "outputs": [], + "source": [ + "# open multiple files\n", + "n_files = 3\n", + "full_paths = [os.path.join(datastream_dir, f_path) for f_path in file_sorted_reverse[:n_files]]\n", + "print(full_paths)\n", + "try: # Note: sometimes multiple files cannot be merged, so we used try except here.\n", + " ds_mutiple = xr.open_mfdataset(full_paths)\n", + " # ds_mutiple\n", + " print(type(ds_mutiple))\n", + " print(ds_mutiple)\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "3fa59943", + "metadata": {}, + "source": [ + "## Explore the data" + ] + }, + { + "cell_type": "markdown", + "id": "571f69b9", + "metadata": {}, + "source": [ + "### NetCDF Data structure \n", + "\n", + "\n", + "(If you are confident with NetCDF basics and xarray essentials, feel free to skip this session.)\n", + "Before we dive into data exploration, there are some eseential concpets we should be familiar with (shown below): \n", + "* Dataset\n", + "* Data array\n", + "* Variable\n", + "* Dimenssion\n", + "* Coordinate\n", + "* Data Type\n", + "* Meta Data (Attributes)\n", + "\n", + "We will not go into details about NetCDF basics and here are some references you might find helpful\n", + "* [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#)\n", + "* [Network Common Data Form (NetCDF)](https://www.unidata.ucar.edu/software/netcdf/) and [A Brief History of (netCDF) Time](https://www.unidata.ucar.edu/software/netcdf/time/recs.html)\n", + "\n", + "\n", + "\n", + "### Xarray essentials\n", + "Earlier we introduced the xarray pacakge and used `open_dataset` and `open_mfdataset` to retrieve NetCDF data file as an xarray Dataset object. Recall that you can review the data in a notebook by using `print(ds)` or `ds`. \n", + "\n", + "Using xarray to retrieve the aforementioned NetCDF basics is straightforwared. In general,\n", + "* Dataset: `ds`\n", + "* Data array: `ds.variables`\n", + "* Variable: `ds.ds.variables`\n", + "* Dimenssion: `ds.dims`\n", + "* Coordinate: `ds.coords`\n", + "* Data Type: `type`\n", + "* Meta Data (Attributes)\n", + "\n", + "Also, here are some references if you are new to xarray\n", + "* [Xarray in 45 minutes](https://tutorial.xarray.dev/overview/xarray-in-45-min.html)\n", + "* [Handling NetCDF Files using XArray for Absolute Beginners](https://towardsdatascience.com/handling-netcdf-files-using-xarray-for-absolute-beginners-111a8ab4463f)\n", + "\n", + "Try the following commands in action\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b3eac323", + "metadata": {}, + "outputs": [], + "source": [ + "ds = ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77ecf85d", + "metadata": {}, + "outputs": [], + "source": [ + "# Dataset \n", + "ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25e7de09", + "metadata": {}, + "outputs": [], + "source": [ + "# Data array, Variable\n", + "# Note: the info can be overwhelming. Do not attempt to grasp everything at the first glance.\n", + "ds.variables" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c41b67e", + "metadata": {}, + "outputs": [], + "source": [ + "# dimenssions\n", + "ds.dims" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "156f1dfc", + "metadata": {}, + "outputs": [], + "source": [ + "# coordinates\n", + "ds.coords" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "277d6064", + "metadata": {}, + "outputs": [], + "source": [ + "# Meta Data (Attributes)\n", + "ds.attrs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d334681f", + "metadata": {}, + "outputs": [], + "source": [ + "# type\n", + "print(type(ds))\n", + "print(type(ds.variables))\n", + "print(type(ds.dims))\n", + "print(type(ds.coords))\n", + "print(type(ds.attrs))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "643399d6", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: retrieve variable names only\n", + "list(ds.variables)" + ] + }, + { + "cell_type": "markdown", + "id": "1cf36878", + "metadata": {}, + "source": [ + "#### Discussion: variable vs. coordinates vs. dimenssions. \n", + "From [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#):\n", + "\n", + "* Dimession: A dimension may be used to represent a real physical dimension, for example, time, latitude, longitude, or height. A dimension might also be used to index other quantities, for example station or model-run-number.\n", + "A netCDF dimension has both a name and a length. A dimension length is an arbitrary positive integer, except that one dimension in a netCDF dataset can have the length UNLIMITED.\n", + "\n", + "* Variables: Variables are used to store the bulk of the data in a netCDF dataset. A variable represents an array of values of the same type. A scalar value is treated as a 0-dimensional array. A variable has a name, a data type, and a shape described by its list of dimensions specified when the variable is created. A variable may also have associated attributes, which may be added, deleted or changed after the variable is created.\n", + "\n", + "* Coordinate (Variables): It is legal for a variable to have the same name as a dimension. Such variables have no special meaning to the netCDF library. However there is a convention that such variables should be treated in a special way by software using this library.\n", + "A variable with the same name as a dimension is called a coordinate variable. It typically defines a physical coordinate corresponding to that dimension.\n", + "\n", + "\n", + "Tips: By definition, coordinates and dimenssions are also variable. For example ds.time is a coordinate but it is also a special variable. But in practice, when we talk about variable, it implies regular/non-coordinate variable. We will use this convention for the remaining notebook. " + ] + }, + { + "cell_type": "markdown", + "id": "d47120f9", + "metadata": {}, + "source": [ + "### Xarray Variable\n", + "\n", + "\n", + "We can use `ds.variables` to access varialbes of a dataset. But many times viewing print-out the whole datasets can be overwhemling. Instead, we would work on the individual variable (also called Xarray data array.) using the `ds[var_name]` syntax, where `var_name` is the name of the variable. See the following examples in action.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5a57e136", + "metadata": {}, + "outputs": [], + "source": [ + "print(type(ds[\"time\"]))\n", + "ds[\"time\"]" + ] + }, + { + "cell_type": "markdown", + "id": "51ec643e", + "metadata": {}, + "source": [ + "#### Variable properties\n", + "For individual varible, the following properties are mostly used with the assocaited xarray method to retrieve them. (assuming var = ds[var_name])\n", + "* name: `var.name`\n", + "* data content: `var.data`\n", + "* attributes: `var.attrs`\n", + "* dimenstions: `var.dims`\n", + "* data type: `var.data.dtype`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a632a525", + "metadata": {}, + "outputs": [], + "source": [ + "var_name = \"time\"\n", + "var = ds[var_name]\n", + "\n", + "print(\"var.name: \\n\", var.name, \"\\n\")\n", + "print(\"var.data: \\n\", var.data, \"\\n\")\n", + "print(\"var.attrs: \\n\", var.attrs, \"\\n\")\n", + "print(\"var.dims: \\n\", var.dims, \"\\n\")\n", + "print(\"var.data.dtype: \\n\", var.data.dtype, \"\\n\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b256b07f", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: indivdual variable info table (collect and display variable info as a tabular format)\n", + "df_info = pd.DataFrame()\n", + "df_info[\"var_name\"] = list(ds.variables)\n", + "df_info[\"dims\"] = df_info.var_name.apply(lambda x: ds[x].dims)\n", + "df_info[\"is_dim\"] = df_info.var_name.apply(lambda x: x in ds.dims)\n", + "df_info[\"n_dim\"] = df_info.var_name.apply(lambda x: len(ds[x].dims))\n", + "df_info[\"attrs\"] = df_info.var_name.apply(lambda x: ds[x].attrs)\n", + "df_info[\"dtype\"] = df_info.var_name.apply(lambda x: ds[x].data.dtype)\n", + "df_info" + ] + }, + { + "cell_type": "markdown", + "id": "e6a36b96", + "metadata": {}, + "source": [ + "### Data cleaning/Preprocessing (skipped)\n", + "Data cleaning and preprocessing is an important stage in the the data analysis pipeline. However it will be out of the scope of this notebook. For more information about data cleaning and preprocessing basics, here are some references. \n", + "* [Xarray Fundamentals](https://earth-env-data-science.github.io/lectures/xarray/xarray.html)\n", + "* [Xarray Tutorial — Pangeo Gallery documentation](http://gallery.pangeo.io/repos/pangeo-data/pangeo-tutorial-gallery/xarray.html)\n", + "* [Pythonic Data Cleaning With pandas and NumPy](https://realpython.com/python-data-cleaning-numpy-pandas/)\n", + "* [Pandas - Cleaning Data](https://www.w3schools.com/python/pandas/pandas_cleaning.asp)" + ] + }, + { + "cell_type": "markdown", + "id": "ae6e1a69", + "metadata": {}, + "source": [ + "### Plotting\n", + "Data visualization (or plotting) is another important data analysis topic and deserves its own discussion. Here in this notebook we will only demonstrate basic tool for simple data visualization tasks.\n", + "\n", + "Here are some reference you might find useful:\n", + "* xarray plotting: https://docs.xarray.dev/en/stable/user-guide/plotting.html\n", + "* Atmospheric Community Toolkit (ACT): https://arm-doe.github.io/ACT/index.html\n", + "\n", + "Note: this notebook is auto-generated using a template. It uses a general idea to select variable(s) to plot and is not customized to each indivdual notebook. Feel free to change the variables in intrrests, especially certain figure is failed to plot." + ] + }, + { + "cell_type": "markdown", + "id": "7e417fb2", + "metadata": {}, + "source": [ + "#### 1-dimenssional basic time series plot\n", + "\n", + "For the following plot we would like to find variables such that\n", + "* it has one and only one dimession\n", + "* \"time\" is its coordinate variable\n", + "* it is not a dimenssion itself,\n", + "* it is not a special variable with substrings within [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "\n", + "Please see the following example in action. (For more details about pandas filtering, please see the following references.)\n", + "* [pandas: multiple conditions while indexing data frame](https://stackoverflow.com/questions/22591174/pandas-multiple-conditions-while-indexing-data-frame-unexpected-behavior)\n", + "* [pandas.DataFrame.apply](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.apply.html)\n", + "* [How to test if a string contains one of the substrings in a list...](https://stackoverflow.com/questions/26577516/how-to-test-if-a-string-contains-one-of-the-substrings-in-a-list-in-pandas" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30edac2d", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==1, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter = df_info[(df_info.n_dim == 1) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4777a659", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_1d = df_filter.var_name.values[0]\n", + " var_1d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb5ae985", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " ds[var_1d].plot()\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "9e41066e", + "metadata": {}, + "source": [ + "#### 2-dimenssional basic plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09bd4adc", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==2, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_2 = df_info[(df_info.n_dim == 2) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2cdea1a9", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_2d = df_filter_2.var_name.values[0]\n", + " var_2d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5f48879", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " print(ds[var_2d].dims)\n", + " # ds[var_2d].plot()\n", + "\n", + " # conventionally, use \"time\" as x-axis\n", + " ds[var_2d].plot(x=\"time\")\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "7554b2a6", + "metadata": {}, + "source": [ + "#### qc-plotting (optional)\n", + "\n", + "Note: act qc plotting has more strict requirement, one of them is the associated qc variable need to have \"flag_masks\" attributes. Which is added by using ds = act.io.armfiles.read_netcdf(files_list), then ds.clean.cleanup()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71c2096f", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " ds_act = act.io.armfiles.read_netcdf(full_path)\n", + " print(type(ds_act))\n", + " ds_act.clean.cleanup()\n", + "\n", + " # or \n", + " # ds.clean.cleanup()\n", + "except Exception as e:\n", + " print(\"ERROR\", e)\n", + " ds_act = ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "844f8505", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter valid variables for ACT qc plotting\n", + "condition = (\"qc_\" + df_info.var_name).apply(lambda x: ds_act[x].attrs.get(\"flag_masks\") is not None \n", + " if x in list(ds.variables) else False)\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_3 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings))) &\n", + " condition\n", + " ]\n", + "df_filter_3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd38cc6", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "try:\n", + " qc_variable = df_filter_3.var_name.values[0]\n", + " print(qc_variable)\n", + "except Exception as e:\n", + " print(e)\n", + " \n", + "try:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + " qc_ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "d76e4d27", + "metadata": {}, + "source": [ + "#### bonus: choose variables to plot from a dropdown menu " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb733804", + "metadata": {}, + "outputs": [], + "source": [ + "# Valid variables filtering\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_4 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82d5c20d", + "metadata": {}, + "outputs": [], + "source": [ + "# example 1: using xarray plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2fc9aaa4", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# fig, ax = plt.subplots(figsize=(10, 4))\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "# fig.clear() # Remove old lines from plot and plot new one\n", + "# if len(ds[var].dims)==2:\n", + "# ds[var].plot(x=\"time\", add_colorbar=False)\n", + "# else:\n", + "# ds[var].plot()\n", + "# plt.grid()\n", + "# plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fabdd802", + "metadata": {}, + "outputs": [], + "source": [ + "# example 2: using act plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "962b4186", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "\n", + "\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "\n", + "# i_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + "# i_display.add_subplots((1,), figsize=(10, 4))\n", + "# ax = i_display.plot(var, subplot_index=(0,), set_title=f\"{var} ({ds_act[var].attrs['long_name']})\",)\n", + "\n", + "# ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "# ax.grid()\n", + "# plt.show()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/PSAP/aospsap3w.c1.ipynb b/VAPs/quicklook/PSAP/aospsap3w.c1.ipynb new file mode 100644 index 00000000..32b774ed --- /dev/null +++ b/VAPs/quicklook/PSAP/aospsap3w.c1.ipynb @@ -0,0 +1,339 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# AOSPSAP3W.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/psap) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'aospsap3w'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2013-06-24', 'facility': 'M1', 'site': 'pvc', 'start_date': '2012-07-16'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'pvc', 'M1' )\n", + "\n", + "date_start = '2013-06-22'\n", + "date_end = '2013-06-24'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['transmittance_blue', 'dqrvar_transmittance_blue', 'transmittance_green']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'transmittance_blue'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'transmittance_blue'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/QCRAD/QCRAD_tutorial.ipynb b/VAPs/quicklook/QCRAD/QCRAD_tutorial.ipynb new file mode 100644 index 00000000..cc100e4a --- /dev/null +++ b/VAPs/quicklook/QCRAD/QCRAD_tutorial.ipynb @@ -0,0 +1,838 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# QCRAD1LONG.C1 Notebook\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/qcrad) for more information about this vap." + ] + }, + { + "cell_type": "markdown", + "id": "97097763", + "metadata": {}, + "source": [ + "In this notebook, we demonstrate the workflow to explore ARM vap data (using qcrad1long as an example.) Value-added products (VAPs) are higher-order data products that have been analyzed and processed to ease scientists’ use of ARM data in atmospheric research and global climate models. \n", + "Here is the main content we will cover." + ] + }, + { + "cell_type": "markdown", + "id": "eddec40f", + "metadata": {}, + "source": [ + "# Table of Content\n", + "## Access the data\n", + "* How to retrieve the data\n", + "* Data path and file name conventions\n", + "* Load data\n", + "## Explore the data\n", + "* NetCDF Data structure\n", + "* Xarray essentials\n", + "* Xarray Variable\n", + "## Plot the data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "import random\n", + "\n", + "import glob\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "2372d5be", + "metadata": {}, + "source": [ + "## Access the data" + ] + }, + { + "cell_type": "markdown", + "id": "5b0d9684", + "metadata": {}, + "source": [ + "### How to retrieve the data\n", + "We assume the path \"/data/archive\" is available where you are running this notebook. You can use `os.path.exists(\"/data/archive\")` to verify if the path exists at your machine.\n", + "\n", + "\n", + "### Data path and file name conventions\n", + "There several common terminologies regarding ARM data, for example, data-stream-name, data-level, site, facility, instrument, etc. (For more details, please see [ARM Data File Standards Version 1.2](https://www.google.com/search?q=arm+datastream+facility+cite+definition&rlz=1C1GCEJ_enUS1029US1029&ei=hb41ZICrBPukqtsPvMODIA&ved=0ahUKEwjAgeiS0aL-AhV7kmoFHbzhAAQQ4dUDCBA&uact=5&oq=arm+datastream+facility+cite+definition&gs_lcp=Cgxnd3Mtd2l6LXNlcnAQAzIFCAAQogQyBQgAEKIEMgUIABCiBDIFCAAQogQyBQgAEKIEOgoIABBHENYEELADOgoIIRCgARDDBBAKSgQIQRgAUJ4DWIoOYO0PaAFwAXgAgAGMAYgBuQmSAQMyLjmYAQCgAQHIAQjAAQE&sclient=gws-wiz-serp).) \n", + "\n", + "For example, this notebook is called `qcrad1long.c1`, where `qcrad1long` is the \"datastream name\", and the `{process.ds_class_level}}` is the \"data level\".\n", + "\n", + "This datastream also contains site `awr` and facility `S1`. (Note: individual datastream might have multiple site-facility pairs.)\n", + "In such a case, the data of this data-stream is stored at `/data/archive/awr/awrqcrad1longS1.c1`, which is in the format of `//.`. We can use the following method to assign the data-stream directory `datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )`\n", + "\n", + "The data files under datastream_dir also follows naming conventions. But once reach the datastream_dir level, the most import file naming convention to differentiate the files is \"yyyyMMdd.hhmmss\", which comes handy to filter out files based on datetime. For example, we can use `glob.glob(f'{datastream_dir}/*.200709*.*')` to filter files in 2007 September.\n", + "\n", + "(Note: refer to the https://adc.arm.gov/discovery/#/ and https://adc.arm.gov/solr8/metadata/select API to explore ARM datastream and assoicated available sites and facilities\n", + "\n", + "Please see the following examples in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d7e9eb85", + "metadata": {}, + "outputs": [], + "source": [ + "# Verify if DATA_DIR path exists\n", + "DATA_DIR = \"/data/archive\"\n", + "os.path.exists(DATA_DIR)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "586993fd", + "metadata": {}, + "outputs": [], + "source": [ + "# Speicify datastream_dir following the path conventions and check its existence\n", + "DATASTREAM_NAME = \"qcrad1long\"\n", + "DATA_LEVEL = \"c1\"\n", + "site = \"awr\"\n", + "facility = \"S1\"\n", + "datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )\n", + "print(datastream_dir)\n", + "print(os.path.exists(datastream_dir))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0742f7c1", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: list 5 (random) files under datastream_dir\n", + "files = os.listdir(datastream_dir)\n", + "files[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39b98a36", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: get most recent file\n", + "list_of_files = glob.glob(f\"{datastream_dir}/*\") # * means all if need specific format then *.csv\n", + "latest_file = max(list_of_files, key=os.path.getctime)\n", + "latest_file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "902d514e", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus sort datastream files based on datetime\n", + "files = os.listdir(datastream_dir)\n", + "file_sorted = files.copy()\n", + "file_sorted.sort() \n", + "print(file_sorted[:5])\n", + "\n", + "# to reverse\n", + "file_sorted_reverse = files.copy() \n", + "file_sorted_reverse.sort(reverse=True)\n", + "print(file_sorted_reverse[:5])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec5923b2", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: pattern matching\n", + "# filter the 200709** files under datastream_dir\n", + "files_filter = glob.glob(f'{datastream_dir}/*.200709*.*')\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c4a4aa18", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: use armnotebook_utils.file_filter (TODO) to filter files based on datastream info \n" + ] + }, + { + "cell_type": "markdown", + "id": "cfeb9efc", + "metadata": {}, + "source": [ + "### Load data\n", + "The arm data is stored in [NetCDF](https://en.wikipedia.org/wiki/NetCDF#:~:text=%22NetCDF%20(network%20Common%20Data%20Form,format%20for%20representing%20scientific%20data.) format. We can use xarray's `open_dataset` method to load single file, or `open_mfdataset` to open multiple files. (Note: the latter will still return a single xarray dataset object by combining multiple files.)\n", + "\n", + "Note: open_dataset keeps the file handle open and lazy loads its contents. All parameters are passed directly to open_dataset. It is a preferable method over load_dataset for memory-effiency. (more details, please see [xarray.open_dataset](https://docs.xarray.dev/en/stable/generated/xarray.open_dataset.html))\n", + "\n", + "See the following example in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a440a329", + "metadata": {}, + "outputs": [], + "source": [ + "# open single file\n", + "full_path = latest_file\n", + "print(full_path)\n", + "ds_single = xr.open_dataset(full_path)\n", + "ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0143a3d", + "metadata": {}, + "outputs": [], + "source": [ + "print(ds_single)\n", + "print(type(ds_single))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c0f8939", + "metadata": {}, + "outputs": [], + "source": [ + "# open multiple files\n", + "n_files = 3\n", + "full_paths = [os.path.join(datastream_dir, f_path) for f_path in file_sorted_reverse[:n_files]]\n", + "print(full_paths)\n", + "try: # Note: sometimes multiple files cannot be merged, so we used try except here.\n", + " ds_mutiple = xr.open_mfdataset(full_paths)\n", + " # ds_mutiple\n", + " print(type(ds_mutiple))\n", + " print(ds_mutiple)\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "3fa59943", + "metadata": {}, + "source": [ + "## Explore the data" + ] + }, + { + "cell_type": "markdown", + "id": "571f69b9", + "metadata": {}, + "source": [ + "### NetCDF Data structure \n", + "\n", + "\n", + "(If you are confident with NetCDF basics and xarray essentials, feel free to skip this session.)\n", + "Before we dive into data exploration, there are some eseential concpets we should be familiar with (shown below): \n", + "* Dataset\n", + "* Data array\n", + "* Variable\n", + "* Dimenssion\n", + "* Coordinate\n", + "* Data Type\n", + "* Meta Data (Attributes)\n", + "\n", + "We will not go into details about NetCDF basics and here are some references you might find helpful\n", + "* [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#)\n", + "* [Network Common Data Form (NetCDF)](https://www.unidata.ucar.edu/software/netcdf/) and [A Brief History of (netCDF) Time](https://www.unidata.ucar.edu/software/netcdf/time/recs.html)\n", + "\n", + "\n", + "\n", + "### Xarray essentials\n", + "Earlier we introduced the xarray pacakge and used `open_dataset` and `open_mfdataset` to retrieve NetCDF data file as an xarray Dataset object. Recall that you can review the data in a notebook by using `print(ds)` or `ds`. \n", + "\n", + "Using xarray to retrieve the aforementioned NetCDF basics is straightforwared. In general,\n", + "* Dataset: `ds`\n", + "* Data array: `ds.variables`\n", + "* Variable: `ds.ds.variables`\n", + "* Dimenssion: `ds.dims`\n", + "* Coordinate: `ds.coords`\n", + "* Data Type: `type`\n", + "* Meta Data (Attributes)\n", + "\n", + "Also, here are some references if you are new to xarray\n", + "* [Xarray in 45 minutes](https://tutorial.xarray.dev/overview/xarray-in-45-min.html)\n", + "* [Handling NetCDF Files using XArray for Absolute Beginners](https://towardsdatascience.com/handling-netcdf-files-using-xarray-for-absolute-beginners-111a8ab4463f)\n", + "\n", + "Try the following commands in action\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b3eac323", + "metadata": {}, + "outputs": [], + "source": [ + "ds = ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77ecf85d", + "metadata": {}, + "outputs": [], + "source": [ + "# Dataset \n", + "ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25e7de09", + "metadata": {}, + "outputs": [], + "source": [ + "# Data array, Variable\n", + "# Note: the info can be overwhelming. Do not attempt to grasp everything at the first glance.\n", + "ds.variables" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c41b67e", + "metadata": {}, + "outputs": [], + "source": [ + "# dimenssions\n", + "ds.dims" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "156f1dfc", + "metadata": {}, + "outputs": [], + "source": [ + "# coordinates\n", + "ds.coords" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "277d6064", + "metadata": {}, + "outputs": [], + "source": [ + "# Meta Data (Attributes)\n", + "ds.attrs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d334681f", + "metadata": {}, + "outputs": [], + "source": [ + "# type\n", + "print(type(ds))\n", + "print(type(ds.variables))\n", + "print(type(ds.dims))\n", + "print(type(ds.coords))\n", + "print(type(ds.attrs))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "643399d6", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: retrieve variable names only\n", + "list(ds.variables)" + ] + }, + { + "cell_type": "markdown", + "id": "1cf36878", + "metadata": {}, + "source": [ + "#### Discussion: variable vs. coordinates vs. dimenssions. \n", + "From [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#):\n", + "\n", + "* Dimession: A dimension may be used to represent a real physical dimension, for example, time, latitude, longitude, or height. A dimension might also be used to index other quantities, for example station or model-run-number.\n", + "A netCDF dimension has both a name and a length. A dimension length is an arbitrary positive integer, except that one dimension in a netCDF dataset can have the length UNLIMITED.\n", + "\n", + "* Variables: Variables are used to store the bulk of the data in a netCDF dataset. A variable represents an array of values of the same type. A scalar value is treated as a 0-dimensional array. A variable has a name, a data type, and a shape described by its list of dimensions specified when the variable is created. A variable may also have associated attributes, which may be added, deleted or changed after the variable is created.\n", + "\n", + "* Coordinate (Variables): It is legal for a variable to have the same name as a dimension. Such variables have no special meaning to the netCDF library. However there is a convention that such variables should be treated in a special way by software using this library.\n", + "A variable with the same name as a dimension is called a coordinate variable. It typically defines a physical coordinate corresponding to that dimension.\n", + "\n", + "\n", + "Tips: By definition, coordinates and dimenssions are also variable. For example ds.time is a coordinate but it is also a special variable. But in practice, when we talk about variable, it implies regular/non-coordinate variable. We will use this convention for the remaining notebook. " + ] + }, + { + "cell_type": "markdown", + "id": "d47120f9", + "metadata": {}, + "source": [ + "### Xarray Variable\n", + "\n", + "\n", + "We can use `ds.variables` to access varialbes of a dataset. But many times viewing print-out the whole datasets can be overwhemling. Instead, we would work on the individual variable (also called Xarray data array.) using the `ds[var_name]` syntax, where `var_name` is the name of the variable. See the following examples in action.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5a57e136", + "metadata": {}, + "outputs": [], + "source": [ + "print(type(ds[\"time\"]))\n", + "ds[\"time\"]" + ] + }, + { + "cell_type": "markdown", + "id": "51ec643e", + "metadata": {}, + "source": [ + "#### Variable properties\n", + "For individual varible, the following properties are mostly used with the assocaited xarray method to retrieve them. (assuming var = ds[var_name])\n", + "* name: `var.name`\n", + "* data content: `var.data`\n", + "* attributes: `var.attrs`\n", + "* dimenstions: `var.dims`\n", + "* data type: `var.data.dtype`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a632a525", + "metadata": {}, + "outputs": [], + "source": [ + "var_name = \"time\"\n", + "var = ds[var_name]\n", + "\n", + "print(\"var.name: \\n\", var.name, \"\\n\")\n", + "print(\"var.data: \\n\", var.data, \"\\n\")\n", + "print(\"var.attrs: \\n\", var.attrs, \"\\n\")\n", + "print(\"var.dims: \\n\", var.dims, \"\\n\")\n", + "print(\"var.data.dtype: \\n\", var.data.dtype, \"\\n\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b256b07f", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: indivdual variable info table (collect and display variable info as a tabular format)\n", + "df_info = pd.DataFrame()\n", + "df_info[\"var_name\"] = list(ds.variables)\n", + "df_info[\"dims\"] = df_info.var_name.apply(lambda x: ds[x].dims)\n", + "df_info[\"is_dim\"] = df_info.var_name.apply(lambda x: x in ds.dims)\n", + "df_info[\"n_dim\"] = df_info.var_name.apply(lambda x: len(ds[x].dims))\n", + "df_info[\"attrs\"] = df_info.var_name.apply(lambda x: ds[x].attrs)\n", + "df_info[\"dtype\"] = df_info.var_name.apply(lambda x: ds[x].data.dtype)\n", + "df_info" + ] + }, + { + "cell_type": "markdown", + "id": "e6a36b96", + "metadata": {}, + "source": [ + "### Data cleaning/Preprocessing (skipped)\n", + "Data cleaning and preprocessing is an important stage in the the data analysis pipeline. However it will be out of the scope of this notebook. For more information about data cleaning and preprocessing basics, here are some references. \n", + "* [Xarray Fundamentals](https://earth-env-data-science.github.io/lectures/xarray/xarray.html)\n", + "* [Xarray Tutorial — Pangeo Gallery documentation](http://gallery.pangeo.io/repos/pangeo-data/pangeo-tutorial-gallery/xarray.html)\n", + "* [Pythonic Data Cleaning With pandas and NumPy](https://realpython.com/python-data-cleaning-numpy-pandas/)\n", + "* [Pandas - Cleaning Data](https://www.w3schools.com/python/pandas/pandas_cleaning.asp)" + ] + }, + { + "cell_type": "markdown", + "id": "ae6e1a69", + "metadata": {}, + "source": [ + "### Plotting\n", + "Data visualization (or plotting) is another important data analysis topic and deserves its own discussion. Here in this notebook we will only demonstrate basic tool for simple data visualization tasks.\n", + "\n", + "Here are some reference you might find useful:\n", + "* xarray plotting: https://docs.xarray.dev/en/stable/user-guide/plotting.html\n", + "* Atmospheric Community Toolkit (ACT): https://arm-doe.github.io/ACT/index.html\n", + "\n", + "Note: this notebook is auto-generated using a template. It uses a general idea to select variable(s) to plot and is not customized to each indivdual notebook. Feel free to change the variables in intrrests, especially certain figure is failed to plot." + ] + }, + { + "cell_type": "markdown", + "id": "7e417fb2", + "metadata": {}, + "source": [ + "#### 1-dimenssional basic time series plot\n", + "\n", + "For the following plot we would like to find variables such that\n", + "* it has one and only one dimession\n", + "* \"time\" is its coordinate variable\n", + "* it is not a dimenssion itself,\n", + "* it is not a special variable with substrings within [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "\n", + "Please see the following example in action. (For more details about pandas filtering, please see the following references.)\n", + "* [pandas: multiple conditions while indexing data frame](https://stackoverflow.com/questions/22591174/pandas-multiple-conditions-while-indexing-data-frame-unexpected-behavior)\n", + "* [pandas.DataFrame.apply](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.apply.html)\n", + "* [How to test if a string contains one of the substrings in a list...](https://stackoverflow.com/questions/26577516/how-to-test-if-a-string-contains-one-of-the-substrings-in-a-list-in-pandas" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30edac2d", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==1, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter = df_info[(df_info.n_dim == 1) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4777a659", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_1d = df_filter.var_name.values[0]\n", + " var_1d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb5ae985", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " ds[var_1d].plot()\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "9e41066e", + "metadata": {}, + "source": [ + "#### 2-dimenssional basic plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09bd4adc", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==2, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_2 = df_info[(df_info.n_dim == 2) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2cdea1a9", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_2d = df_filter_2.var_name.values[0]\n", + " var_2d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5f48879", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " print(ds[var_2d].dims)\n", + " # ds[var_2d].plot()\n", + "\n", + " # conventionally, use \"time\" as x-axis\n", + " ds[var_2d].plot(x=\"time\")\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "7554b2a6", + "metadata": {}, + "source": [ + "#### qc-plotting (optional)\n", + "\n", + "Note: act qc plotting has more strict requirement, one of them is the associated qc variable need to have \"flag_masks\" attributes. Which is added by using ds = act.io.armfiles.read_netcdf(files_list), then ds.clean.cleanup()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71c2096f", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " ds_act = act.io.armfiles.read_netcdf(full_path)\n", + " print(type(ds_act))\n", + " ds_act.clean.cleanup()\n", + "\n", + " # or \n", + " # ds.clean.cleanup()\n", + "except Exception as e:\n", + " print(\"ERROR\", e)\n", + " ds_act = ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "844f8505", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter valid variables for ACT qc plotting\n", + "condition = (\"qc_\" + df_info.var_name).apply(lambda x: ds_act[x].attrs.get(\"flag_masks\") is not None \n", + " if x in list(ds.variables) else False)\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_3 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings))) &\n", + " condition\n", + " ]\n", + "df_filter_3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd38cc6", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "try:\n", + " qc_variable = df_filter_3.var_name.values[0]\n", + " print(qc_variable)\n", + "except Exception as e:\n", + " print(e)\n", + " \n", + "try:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + " qc_ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "d76e4d27", + "metadata": {}, + "source": [ + "#### bonus: choose variables to plot from a dropdown menu " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb733804", + "metadata": {}, + "outputs": [], + "source": [ + "# Valid variables filtering\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_4 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82d5c20d", + "metadata": {}, + "outputs": [], + "source": [ + "# example 1: using xarray plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2fc9aaa4", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# fig, ax = plt.subplots(figsize=(10, 4))\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "# fig.clear() # Remove old lines from plot and plot new one\n", + "# if len(ds[var].dims)==2:\n", + "# ds[var].plot(x=\"time\", add_colorbar=False)\n", + "# else:\n", + "# ds[var].plot()\n", + "# plt.grid()\n", + "# plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fabdd802", + "metadata": {}, + "outputs": [], + "source": [ + "# example 2: using act plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "962b4186", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "\n", + "\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "\n", + "# i_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + "# i_display.add_subplots((1,), figsize=(10, 4))\n", + "# ax = i_display.plot(var, subplot_index=(0,), set_title=f\"{var} ({ds_act[var].attrs['long_name']})\",)\n", + "\n", + "# ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "# ax.grid()\n", + "# plt.show()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/QCRAD/qcrad1long.c1.ipynb b/VAPs/quicklook/QCRAD/qcrad1long.c1.ipynb new file mode 100644 index 00000000..f8779930 --- /dev/null +++ b/VAPs/quicklook/QCRAD/qcrad1long.c1.ipynb @@ -0,0 +1,339 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# QCRAD1LONG.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/qcrad) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'qcrad1long'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2016-01-19', 'facility': 'S1', 'site': 'awr', 'start_date': '2015-12-05'}, {'end_date': '2020-08-06', 'facility': 'M1', 'site': 'oli', 'start_date': '2019-08-26'}, {'end_date': '2023-12-18', 'facility': 'C1', 'site': 'ena', 'start_date': '2022-10-29'}, {'end_date': '2008-01-01', 'facility': 'M1', 'site': 'fkb', 'start_date': '2007-03-01'}, {'end_date': '2015-12-01', 'facility': 'M1', 'site': 'mao', 'start_date': '2014-01-01'}, {'end_date': '2008-12-01', 'facility': 'M1', 'site': 'hfe', 'start_date': '2008-06-01'}, {'end_date': '2012-02-01', 'facility': 'M1', 'site': 'gan', 'start_date': '2011-10-01'}, {'end_date': '2011-01-01', 'facility': 'M1', 'site': 'grw', 'start_date': '2009-04-01'}, {'end_date': '2013-07-08', 'facility': 'M1', 'site': 'pvc', 'start_date': '2012-06-26'}, {'end_date': '2005-09-01', 'facility': 'M1', 'site': 'pye', 'start_date': '2005-02-01'}, {'end_date': '2011-05-01', 'facility': 'M1', 'site': 'sbs', 'start_date': '2010-09-23'}, {'end_date': '2012-03-27', 'facility': 'M1', 'site': 'pgh', 'start_date': '2011-06-16'}, {'end_date': '2023-12-17', 'facility': 'C1', 'site': 'nsa', 'start_date': '1998-02-15'}, {'end_date': '2011-01-16', 'facility': 'C2', 'site': 'nsa', 'start_date': '1999-08-21'}, {'end_date': '2007-01-01', 'facility': 'M1', 'site': 'nim', 'start_date': '2005-12-01'}, {'end_date': '2020-05-26', 'facility': 'C1', 'site': 'sgp', 'start_date': '2016-10-03'}, {'end_date': '2023-12-18', 'facility': 'E39', 'site': 'sgp', 'start_date': '2015-08-23'}, {'end_date': '2023-09-28', 'facility': 'E40', 'site': 'sgp', 'start_date': '2015-10-07'}, {'end_date': '2023-09-28', 'facility': 'E11', 'site': 'sgp', 'start_date': '2022-08-05'}, {'end_date': '2023-12-17', 'facility': 'E12', 'site': 'sgp', 'start_date': '2019-08-26'}, {'end_date': '2023-12-18', 'facility': 'E13', 'site': 'sgp', 'start_date': '2022-08-10'}, {'end_date': '2023-09-28', 'facility': 'E15', 'site': 'sgp', 'start_date': '2019-08-20'}, {'end_date': '2019-05-01', 'facility': 'E21', 'site': 'sgp', 'start_date': '2018-05-23'}, {'end_date': '2020-06-08', 'facility': 'E31', 'site': 'sgp', 'start_date': '2016-12-02'}, {'end_date': '2023-12-18', 'facility': 'E32', 'site': 'sgp', 'start_date': '2019-06-13'}, {'end_date': '2023-12-18', 'facility': 'E33', 'site': 'sgp', 'start_date': '2017-07-14'}, {'end_date': '2023-09-28', 'facility': 'E34', 'site': 'sgp', 'start_date': '2017-07-15'}, {'end_date': '2023-09-28', 'facility': 'E35', 'site': 'sgp', 'start_date': '2017-07-20'}, {'end_date': '2023-09-28', 'facility': 'E36', 'site': 'sgp', 'start_date': '2019-06-25'}, {'end_date': '2023-12-18', 'facility': 'E37', 'site': 'sgp', 'start_date': '2019-06-21'}, {'end_date': '2019-07-26', 'facility': 'E38', 'site': 'sgp', 'start_date': '2017-07-19'}, {'end_date': '2023-08-02', 'facility': 'E41', 'site': 'sgp', 'start_date': '2016-04-13'}, {'end_date': '2023-09-28', 'facility': 'E9', 'site': 'sgp', 'start_date': '2018-05-31'}, {'end_date': '2023-12-18', 'facility': 'S01', 'site': 'sgp', 'start_date': '2020-05-26'}, {'end_date': '2014-07-06', 'facility': 'C1', 'site': 'twp', 'start_date': '1996-10-01'}, {'end_date': '2011-10-01', 'facility': 'C2', 'site': 'twp', 'start_date': '1998-10-01'}, {'end_date': '2015-01-05', 'facility': 'C3', 'site': 'twp', 'start_date': '2002-03-01'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2020-05-24'\n", + "date_end = '2020-05-26'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['BestEstimate_down_short_hemisp', 'down_short_hemisp', 'down_short_diffuse_hemisp']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'BestEstimate_down_short_hemisp'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'BestEstimate_down_short_hemisp'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/QCRAD/qcrad1long.c2.ipynb b/VAPs/quicklook/QCRAD/qcrad1long.c2.ipynb new file mode 100644 index 00000000..b877f2d1 --- /dev/null +++ b/VAPs/quicklook/QCRAD/qcrad1long.c2.ipynb @@ -0,0 +1,339 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# QCRAD1LONG.C2 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/qcrad) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'qcrad1long'\n", + "DATA_LEVEL = 'c2'\n", + "LOCATIONS = [{'end_date': '2017-01-02', 'facility': 'M1', 'site': 'awr', 'start_date': '2015-11-24'}, {'end_date': '2017-11-01', 'facility': 'M1', 'site': 'asi', 'start_date': '2016-05-05'}, {'end_date': '2020-06-02', 'facility': 'M1', 'site': 'anx', 'start_date': '2019-12-01'}, {'end_date': '2023-06-15', 'facility': 'M1', 'site': 'guc', 'start_date': '2021-09-01'}, {'end_date': '2022-10-28', 'facility': 'C1', 'site': 'ena', 'start_date': '2013-10-03'}, {'end_date': '2022-10-01', 'facility': 'M1', 'site': 'hou', 'start_date': '2021-08-17'}, {'end_date': '2008-01-01', 'facility': 'M1', 'site': 'fkb', 'start_date': '2007-03-15'}, {'end_date': '2015-12-01', 'facility': 'M1', 'site': 'mao', 'start_date': '2013-12-20'}, {'end_date': '2018-03-13', 'facility': 'S1', 'site': 'mcq', 'start_date': '2016-04-04'}, {'end_date': '2008-12-28', 'facility': 'M1', 'site': 'hfe', 'start_date': '2008-05-16'}, {'end_date': '2019-05-01', 'facility': 'M1', 'site': 'cor', 'start_date': '2018-09-23'}, {'end_date': '2012-02-08', 'facility': 'M1', 'site': 'gan', 'start_date': '2011-09-23'}, {'end_date': '2011-01-06', 'facility': 'M1', 'site': 'grw', 'start_date': '2009-04-16'}, {'end_date': '2013-07-09', 'facility': 'M1', 'site': 'pvc', 'start_date': '2012-06-25'}, {'end_date': '2005-09-15', 'facility': 'M1', 'site': 'pye', 'start_date': '2005-02-14'}, {'end_date': '2011-05-02', 'facility': 'M1', 'site': 'sbs', 'start_date': '2010-09-22'}, {'end_date': '2021-06-15', 'facility': 'M1', 'site': 'oli', 'start_date': '2013-10-01'}, {'end_date': '2012-03-27', 'facility': 'M1', 'site': 'pgh', 'start_date': '2011-06-04'}, {'end_date': '2022-08-20', 'facility': 'C1', 'site': 'nsa', 'start_date': '2003-09-20'}, {'end_date': '2011-01-17', 'facility': 'C2', 'site': 'nsa', 'start_date': '2004-11-17'}, {'end_date': '2007-01-07', 'facility': 'M1', 'site': 'nim', 'start_date': '2005-11-26'}, {'end_date': '2020-05-25', 'facility': 'C1', 'site': 'sgp', 'start_date': '1997-03-21'}, {'end_date': '2022-08-05', 'facility': 'E39', 'site': 'sgp', 'start_date': '2015-08-23'}, {'end_date': '2009-10-28', 'facility': 'E3', 'site': 'sgp', 'start_date': '1996-03-06'}, {'end_date': '2022-08-09', 'facility': 'E40', 'site': 'sgp', 'start_date': '2015-10-07'}, {'end_date': '2011-10-19', 'facility': 'E10', 'site': 'sgp', 'start_date': '1995-07-21'}, {'end_date': '2022-08-04', 'facility': 'E11', 'site': 'sgp', 'start_date': '1995-06-30'}, {'end_date': '2022-08-02', 'facility': 'E12', 'site': 'sgp', 'start_date': '1996-01-19'}, {'end_date': '2022-08-09', 'facility': 'E13', 'site': 'sgp', 'start_date': '1994-01-07'}, {'end_date': '2022-08-08', 'facility': 'E15', 'site': 'sgp', 'start_date': '1994-01-12'}, {'end_date': '2011-11-15', 'facility': 'E16', 'site': 'sgp', 'start_date': '1995-06-02'}, {'end_date': '2009-11-17', 'facility': 'E18', 'site': 'sgp', 'start_date': '1996-06-20'}, {'end_date': '2011-05-23', 'facility': 'E19', 'site': 'sgp', 'start_date': '1998-07-08'}, {'end_date': '2009-10-14', 'facility': 'E1', 'site': 'sgp', 'start_date': '1995-11-15'}, {'end_date': '2011-11-17', 'facility': 'E20', 'site': 'sgp', 'start_date': '1994-11-03'}, {'end_date': '2019-05-01', 'facility': 'E21', 'site': 'sgp', 'start_date': '1999-09-11'}, {'end_date': '2009-12-01', 'facility': 'E22', 'site': 'sgp', 'start_date': '1995-03-16'}, {'end_date': '2009-11-14', 'facility': 'E24', 'site': 'sgp', 'start_date': '1995-11-07'}, {'end_date': '2002-04-03', 'facility': 'E25', 'site': 'sgp', 'start_date': '1997-11-12'}, {'end_date': '2009-12-04', 'facility': 'E27', 'site': 'sgp', 'start_date': '2003-05-15'}, {'end_date': '2009-10-20', 'facility': 'E2', 'site': 'sgp', 'start_date': '1996-03-07'}, {'end_date': '2021-09-21', 'facility': 'E31', 'site': 'sgp', 'start_date': '2011-10-13'}, {'end_date': '2022-08-04', 'facility': 'E32', 'site': 'sgp', 'start_date': '2012-02-04'}, {'end_date': '2022-08-01', 'facility': 'E33', 'site': 'sgp', 'start_date': '2011-08-17'}, {'end_date': '2022-08-08', 'facility': 'E34', 'site': 'sgp', 'start_date': '2011-09-02'}, {'end_date': '2022-08-05', 'facility': 'E35', 'site': 'sgp', 'start_date': '2011-10-05'}, {'end_date': '2022-08-01', 'facility': 'E36', 'site': 'sgp', 'start_date': '2011-09-28'}, {'end_date': '2022-08-02', 'facility': 'E37', 'site': 'sgp', 'start_date': '2011-09-28'}, {'end_date': '2021-06-07', 'facility': 'E38', 'site': 'sgp', 'start_date': '2011-08-23'}, {'end_date': '2022-08-02', 'facility': 'E41', 'site': 'sgp', 'start_date': '2016-04-13'}, {'end_date': '2011-09-26', 'facility': 'E4', 'site': 'sgp', 'start_date': '1995-05-08'}, {'end_date': '2009-11-02', 'facility': 'E5', 'site': 'sgp', 'start_date': '1996-06-14'}, {'end_date': '2011-10-18', 'facility': 'E6', 'site': 'sgp', 'start_date': '1996-03-05'}, {'end_date': '2011-11-14', 'facility': 'E7', 'site': 'sgp', 'start_date': '1995-05-18'}, {'end_date': '2009-11-10', 'facility': 'E8', 'site': 'sgp', 'start_date': '1995-09-22'}, {'end_date': '2022-08-03', 'facility': 'E9', 'site': 'sgp', 'start_date': '1994-01-12'}, {'end_date': '2014-07-06', 'facility': 'C1', 'site': 'twp', 'start_date': '1996-10-10'}, {'end_date': '2013-09-08', 'facility': 'C2', 'site': 'twp', 'start_date': '1998-10-29'}, {'end_date': '2015-01-06', 'facility': 'C3', 'site': 'twp', 'start_date': '2002-03-12'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2020-05-23'\n", + "date_end = '2020-05-25'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['BestEstimate_down_short_hemisp', 'down_short_hemisp', 'down_short_diffuse_hemisp']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'BestEstimate_down_short_hemisp'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'BestEstimate_down_short_hemisp'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/QCRAD/qcradbeflux1long.c1.ipynb b/VAPs/quicklook/QCRAD/qcradbeflux1long.c1.ipynb new file mode 100644 index 00000000..6645ebb3 --- /dev/null +++ b/VAPs/quicklook/QCRAD/qcradbeflux1long.c1.ipynb @@ -0,0 +1,339 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# QCRADBEFLUX1LONG.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/qcrad) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'qcradbeflux1long'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2023-12-17', 'facility': 'C1', 'site': 'sgp', 'start_date': '2011-07-18'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2023-12-15'\n", + "date_end = '2023-12-17'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['BestEstimate_down_short_hemisp', 'down_short_hemisp', 'down_short_diffuse_hemisp']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'BestEstimate_down_short_hemisp'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'BestEstimate_down_short_hemisp'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/QCRAD/qcradbeflux1long.c2.ipynb b/VAPs/quicklook/QCRAD/qcradbeflux1long.c2.ipynb new file mode 100644 index 00000000..0cb12c6d --- /dev/null +++ b/VAPs/quicklook/QCRAD/qcradbeflux1long.c2.ipynb @@ -0,0 +1,339 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# QCRADBEFLUX1LONG.C2 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/qcrad) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'qcradbeflux1long'\n", + "DATA_LEVEL = 'c2'\n", + "LOCATIONS = [{'end_date': '2020-05-25', 'facility': 'C1', 'site': 'sgp', 'start_date': '1995-05-19'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2020-05-23'\n", + "date_end = '2020-05-25'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['BestEstimate_down_short_hemisp', 'down_short_hemisp', 'down_short_diffuse_hemisp']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'BestEstimate_down_short_hemisp'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'BestEstimate_down_short_hemisp'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/QCRAD/qcradbrs1long.c1.ipynb b/VAPs/quicklook/QCRAD/qcradbrs1long.c1.ipynb new file mode 100644 index 00000000..2cd5ba7a --- /dev/null +++ b/VAPs/quicklook/QCRAD/qcradbrs1long.c1.ipynb @@ -0,0 +1,339 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# QCRADBRS1LONG.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/qcrad) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'qcradbrs1long'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2023-12-18', 'facility': 'C1', 'site': 'sgp', 'start_date': '1993-09-01'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2023-12-16'\n", + "date_end = '2023-12-18'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['BestEstimate_down_short_hemisp', 'down_short_hemisp', 'down_short_diffuse_hemisp']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'BestEstimate_down_short_hemisp'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'BestEstimate_down_short_hemisp'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/QCRAD/qcradbrs1long.c2.ipynb b/VAPs/quicklook/QCRAD/qcradbrs1long.c2.ipynb new file mode 100644 index 00000000..9ea94c7b --- /dev/null +++ b/VAPs/quicklook/QCRAD/qcradbrs1long.c2.ipynb @@ -0,0 +1,339 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# QCRADBRS1LONG.C2 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/qcrad) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'qcradbrs1long'\n", + "DATA_LEVEL = 'c2'\n", + "LOCATIONS = [{'end_date': '2022-08-09', 'facility': 'C1', 'site': 'sgp', 'start_date': '1993-09-01'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2022-08-07'\n", + "date_end = '2022-08-09'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['BestEstimate_down_short_hemisp', 'down_short_hemisp', 'down_short_diffuse_hemisp']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'BestEstimate_down_short_hemisp'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'BestEstimate_down_short_hemisp'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/RADFLUXANAL/.ipynb_checkpoints/radflux1long.c1-checkpoint.ipynb b/VAPs/quicklook/RADFLUXANAL/.ipynb_checkpoints/radflux1long.c1-checkpoint.ipynb new file mode 100644 index 00000000..771298d9 --- /dev/null +++ b/VAPs/quicklook/RADFLUXANAL/.ipynb_checkpoints/radflux1long.c1-checkpoint.ipynb @@ -0,0 +1,3763 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# RADFLUX1LONG.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/radfluxanal) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'radflux1long'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2023-09-14', 'facility': 'C1', 'site': 'nsa', 'start_date': '2021-07-04'}, {'end_date': '2021-06-12', 'facility': 'M1', 'site': 'oli', 'start_date': '2020-10-16'}, {'end_date': '2023-09-28', 'facility': 'E11', 'site': 'sgp', 'start_date': '2020-06-08'}, {'end_date': '2023-10-30', 'facility': 'E12', 'site': 'sgp', 'start_date': '2020-06-07'}, {'end_date': '2023-10-26', 'facility': 'E13', 'site': 'sgp', 'start_date': '2020-06-03'}, {'end_date': '2023-09-28', 'facility': 'E15', 'site': 'sgp', 'start_date': '2021-07-12'}, {'end_date': '2023-09-28', 'facility': 'E9', 'site': 'sgp', 'start_date': '2021-07-04'}, {'end_date': '2021-09-20', 'facility': 'E31', 'site': 'sgp', 'start_date': '2020-06-08'}, {'end_date': '2023-10-31', 'facility': 'E32', 'site': 'sgp', 'start_date': '2021-07-03'}, {'end_date': '2023-10-31', 'facility': 'E33', 'site': 'sgp', 'start_date': '2020-06-11'}, {'end_date': '2023-09-28', 'facility': 'E34', 'site': 'sgp', 'start_date': '2020-06-18'}, {'end_date': '2023-09-28', 'facility': 'E35', 'site': 'sgp', 'start_date': '2019-06-24'}, {'end_date': '2023-09-28', 'facility': 'E36', 'site': 'sgp', 'start_date': '2020-06-26'}, {'end_date': '2023-10-31', 'facility': 'E37', 'site': 'sgp', 'start_date': '2020-07-01'}, {'end_date': '2021-05-29', 'facility': 'E38', 'site': 'sgp', 'start_date': '2020-06-25'}, {'end_date': '2023-10-30', 'facility': 'E39', 'site': 'sgp', 'start_date': '2020-09-07'}, {'end_date': '2023-09-28', 'facility': 'E40', 'site': 'sgp', 'start_date': '2020-09-07'}, {'end_date': '2023-08-02', 'facility': 'E41', 'site': 'sgp', 'start_date': '2021-07-31'}]" + ] + }, + { + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "ac6764f5", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The following locations and date ranges are available for this VAP:\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
sitefacilitystart_dateend_date
0nsaC12021-07-042023-09-14
1oliM12020-10-162021-06-12
2sgpE112020-06-082023-09-28
3sgpE122020-06-072023-10-30
4sgpE132020-06-032023-10-26
5sgpE152021-07-122023-09-28
6sgpE92021-07-042023-09-28
7sgpE312020-06-082021-09-20
8sgpE322021-07-032023-10-31
9sgpE332020-06-112023-10-31
10sgpE342020-06-182023-09-28
11sgpE352019-06-242023-09-28
12sgpE362020-06-262023-09-28
13sgpE372020-07-012023-10-31
14sgpE382020-06-252021-05-29
15sgpE392020-09-072023-10-30
16sgpE402020-09-072023-09-28
17sgpE412021-07-312023-08-02
\n", + "
" + ], + "text/plain": [ + " site facility start_date end_date\n", + "0 nsa C1 2021-07-04 2023-09-14\n", + "1 oli M1 2020-10-16 2021-06-12\n", + "2 sgp E11 2020-06-08 2023-09-28\n", + "3 sgp E12 2020-06-07 2023-10-30\n", + "4 sgp E13 2020-06-03 2023-10-26\n", + "5 sgp E15 2021-07-12 2023-09-28\n", + "6 sgp E9 2021-07-04 2023-09-28\n", + "7 sgp E31 2020-06-08 2021-09-20\n", + "8 sgp E32 2021-07-03 2023-10-31\n", + "9 sgp E33 2020-06-11 2023-10-31\n", + "10 sgp E34 2020-06-18 2023-09-28\n", + "11 sgp E35 2019-06-24 2023-09-28\n", + "12 sgp E36 2020-06-26 2023-09-28\n", + "13 sgp E37 2020-07-01 2023-10-31\n", + "14 sgp E38 2020-06-25 2021-05-29\n", + "15 sgp E39 2020-09-07 2023-10-30\n", + "16 sgp E40 2020-09-07 2023-09-28\n", + "17 sgp E41 2021-07-31 2023-08-02" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'E11' )\n", + "\n", + "date_start = '2023-09-25'\n", + "date_end = '2023-09-27'" + ] + }, + { + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'/data/archive/sgp/sgpradflux1longE11.c1'" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['20230925', '20230926', '20230927']" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "51feea2e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['/data/archive/sgp/sgpradflux1longE11.c1/sgpradflux1longE11.c1.20230925.070000.nc',\n", + " '/data/archive/sgp/sgpradflux1longE11.c1/sgpradflux1longE11.c1.20230926.070000.nc',\n", + " '/data/archive/sgp/sgpradflux1longE11.c1/sgpradflux1longE11.c1.20230927.070000.nc']" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "3 files loaded\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
<xarray.Dataset>\n",
+       "Dimensions:                                        (time: 4320, bound: 2)\n",
+       "Coordinates:\n",
+       "  * time                                           (time) datetime64[ns] 2023...\n",
+       "Dimensions without coordinates: bound\n",
+       "Data variables: (12/54)\n",
+       "    base_time                                      (time) datetime64[ns] 2023...\n",
+       "    time_offset                                    (time) datetime64[ns] 2023...\n",
+       "    time_bounds                                    (time, bound) object dask.array<chunksize=(1440, 2), meta=np.ndarray>\n",
+       "    downwelling_shortwave                          (time) float32 dask.array<chunksize=(1440,), meta=np.ndarray>\n",
+       "    source_downwelling_shortwave                   (time) int32 dask.array<chunksize=(1440,), meta=np.ndarray>\n",
+       "    qc_downwelling_shortwave                       (time) int32 dask.array<chunksize=(1440,), meta=np.ndarray>\n",
+       "    ...                                             ...\n",
+       "    qc_pressure                                    (time) int32 dask.array<chunksize=(1440,), meta=np.ndarray>\n",
+       "    precipitation                                  (time) float32 dask.array<chunksize=(1440,), meta=np.ndarray>\n",
+       "    qc_precipitation                               (time) int32 dask.array<chunksize=(1440,), meta=np.ndarray>\n",
+       "    lat                                            (time) float32 36.88 ... 3...\n",
+       "    lon                                            (time) float32 -98.29 ... ...\n",
+       "    alt                                            (time) float32 360.0 ... 3...\n",
+       "Attributes: (12/21)\n",
+       "    command_line:            radflux1long -s sgp -f E11 -b 20230901 -e 202310...\n",
+       "    Conventions:             ARM-1.3\n",
+       "    process_version:         radflux1long-3.16.0\n",
+       "    dod_version:             radflux1long-c1-1.6\n",
+       "    input_datastreams:       sgpqcrad1longE11.c1 : 6.6 : 20230629.000000-2023...\n",
+       "    site_id:                 sgp\n",
+       "    ...                      ...\n",
+       "    fitmode_comment:         01 = daily_fit 00 =  1_fit\n",
+       "    history:                 created by user dsmgr on machine prod-proc2.adc....\n",
+       "    _file_dates:             ['20230925', '20230926', '20230927']\n",
+       "    _file_times:             ['070000', '070000', '070000']\n",
+       "    _datastream:             sgpradflux1longE11.c1\n",
+       "    _arm_standards_flag:     1
" + ], + "text/plain": [ + "\n", + "Dimensions: (time: 4320, bound: 2)\n", + "Coordinates:\n", + " * time (time) datetime64[ns] 2023...\n", + "Dimensions without coordinates: bound\n", + "Data variables: (12/54)\n", + " base_time (time) datetime64[ns] 2023...\n", + " time_offset (time) datetime64[ns] 2023...\n", + " time_bounds (time, bound) object dask.array\n", + " downwelling_shortwave (time) float32 dask.array\n", + " source_downwelling_shortwave (time) int32 dask.array\n", + " qc_downwelling_shortwave (time) int32 dask.array\n", + " ... ...\n", + " qc_pressure (time) int32 dask.array\n", + " precipitation (time) float32 dask.array\n", + " qc_precipitation (time) int32 dask.array\n", + " lat (time) float32 36.88 ... 3...\n", + " lon (time) float32 -98.29 ... ...\n", + " alt (time) float32 360.0 ... 3...\n", + "Attributes: (12/21)\n", + " command_line: radflux1long -s sgp -f E11 -b 20230901 -e 202310...\n", + " Conventions: ARM-1.3\n", + " process_version: radflux1long-3.16.0\n", + " dod_version: radflux1long-c1-1.6\n", + " input_datastreams: sgpqcrad1longE11.c1 : 6.6 : 20230629.000000-2023...\n", + " site_id: sgp\n", + " ... ...\n", + " fitmode_comment: 01 = daily_fit 00 = 1_fit\n", + " history: created by user dsmgr on machine prod-proc2.adc....\n", + " _file_dates: ['20230925', '20230926', '20230927']\n", + " _file_times: ['070000', '070000', '070000']\n", + " _datastream: sgpradflux1longE11.c1\n", + " _arm_standards_flag: 1" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['downwelling_shortwave', 'clearsky_downwelling_shortwave', 'downwelling_longwave']" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'downwelling_shortwave'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'downwelling_shortwave'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/RADFLUXANAL/RADFLUXANAL_tutorial.ipynb b/VAPs/quicklook/RADFLUXANAL/RADFLUXANAL_tutorial.ipynb new file mode 100644 index 00000000..89cd6553 --- /dev/null +++ b/VAPs/quicklook/RADFLUXANAL/RADFLUXANAL_tutorial.ipynb @@ -0,0 +1,838 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# RADFLUX1LONG.C1 Notebook\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/radfluxanal) for more information about this vap." + ] + }, + { + "cell_type": "markdown", + "id": "97097763", + "metadata": {}, + "source": [ + "In this notebook, we demonstrate the workflow to explore ARM vap data (using radflux1long as an example.) Value-added products (VAPs) are higher-order data products that have been analyzed and processed to ease scientists’ use of ARM data in atmospheric research and global climate models. \n", + "Here is the main content we will cover." + ] + }, + { + "cell_type": "markdown", + "id": "eddec40f", + "metadata": {}, + "source": [ + "# Table of Content\n", + "## Access the data\n", + "* How to retrieve the data\n", + "* Data path and file name conventions\n", + "* Load data\n", + "## Explore the data\n", + "* NetCDF Data structure\n", + "* Xarray essentials\n", + "* Xarray Variable\n", + "## Plot the data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "import random\n", + "\n", + "import glob\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "2372d5be", + "metadata": {}, + "source": [ + "## Access the data" + ] + }, + { + "cell_type": "markdown", + "id": "5b0d9684", + "metadata": {}, + "source": [ + "### How to retrieve the data\n", + "We assume the path \"/data/archive\" is available where you are running this notebook. You can use `os.path.exists(\"/data/archive\")` to verify if the path exists at your machine.\n", + "\n", + "\n", + "### Data path and file name conventions\n", + "There several common terminologies regarding ARM data, for example, data-stream-name, data-level, site, facility, instrument, etc. (For more details, please see [ARM Data File Standards Version 1.2](https://www.google.com/search?q=arm+datastream+facility+cite+definition&rlz=1C1GCEJ_enUS1029US1029&ei=hb41ZICrBPukqtsPvMODIA&ved=0ahUKEwjAgeiS0aL-AhV7kmoFHbzhAAQQ4dUDCBA&uact=5&oq=arm+datastream+facility+cite+definition&gs_lcp=Cgxnd3Mtd2l6LXNlcnAQAzIFCAAQogQyBQgAEKIEMgUIABCiBDIFCAAQogQyBQgAEKIEOgoIABBHENYEELADOgoIIRCgARDDBBAKSgQIQRgAUJ4DWIoOYO0PaAFwAXgAgAGMAYgBuQmSAQMyLjmYAQCgAQHIAQjAAQE&sclient=gws-wiz-serp).) \n", + "\n", + "For example, this notebook is called `radflux1long.c1`, where `radflux1long` is the \"datastream name\", and the `{process.ds_class_level}}` is the \"data level\".\n", + "\n", + "This datastream also contains site `oli` and facility `M1`. (Note: individual datastream might have multiple site-facility pairs.)\n", + "In such a case, the data of this data-stream is stored at `/data/archive/oli/oliradflux1longM1.c1`, which is in the format of `//.`. We can use the following method to assign the data-stream directory `datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )`\n", + "\n", + "The data files under datastream_dir also follows naming conventions. But once reach the datastream_dir level, the most import file naming convention to differentiate the files is \"yyyyMMdd.hhmmss\", which comes handy to filter out files based on datetime. For example, we can use `glob.glob(f'{datastream_dir}/*.200709*.*')` to filter files in 2007 September.\n", + "\n", + "(Note: refer to the https://adc.arm.gov/discovery/#/ and https://adc.arm.gov/solr8/metadata/select API to explore ARM datastream and assoicated available sites and facilities\n", + "\n", + "Please see the following examples in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d7e9eb85", + "metadata": {}, + "outputs": [], + "source": [ + "# Verify if DATA_DIR path exists\n", + "DATA_DIR = \"/data/archive\"\n", + "os.path.exists(DATA_DIR)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "586993fd", + "metadata": {}, + "outputs": [], + "source": [ + "# Speicify datastream_dir following the path conventions and check its existence\n", + "DATASTREAM_NAME = \"radflux1long\"\n", + "DATA_LEVEL = \"c1\"\n", + "site = \"oli\"\n", + "facility = \"M1\"\n", + "datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )\n", + "print(datastream_dir)\n", + "print(os.path.exists(datastream_dir))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0742f7c1", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: list 5 (random) files under datastream_dir\n", + "files = os.listdir(datastream_dir)\n", + "files[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39b98a36", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: get most recent file\n", + "list_of_files = glob.glob(f\"{datastream_dir}/*\") # * means all if need specific format then *.csv\n", + "latest_file = max(list_of_files, key=os.path.getctime)\n", + "latest_file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "902d514e", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus sort datastream files based on datetime\n", + "files = os.listdir(datastream_dir)\n", + "file_sorted = files.copy()\n", + "file_sorted.sort() \n", + "print(file_sorted[:5])\n", + "\n", + "# to reverse\n", + "file_sorted_reverse = files.copy() \n", + "file_sorted_reverse.sort(reverse=True)\n", + "print(file_sorted_reverse[:5])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec5923b2", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: pattern matching\n", + "# filter the 200709** files under datastream_dir\n", + "files_filter = glob.glob(f'{datastream_dir}/*.200709*.*')\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c4a4aa18", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: use armnotebook_utils.file_filter (TODO) to filter files based on datastream info \n" + ] + }, + { + "cell_type": "markdown", + "id": "cfeb9efc", + "metadata": {}, + "source": [ + "### Load data\n", + "The arm data is stored in [NetCDF](https://en.wikipedia.org/wiki/NetCDF#:~:text=%22NetCDF%20(network%20Common%20Data%20Form,format%20for%20representing%20scientific%20data.) format. We can use xarray's `open_dataset` method to load single file, or `open_mfdataset` to open multiple files. (Note: the latter will still return a single xarray dataset object by combining multiple files.)\n", + "\n", + "Note: open_dataset keeps the file handle open and lazy loads its contents. All parameters are passed directly to open_dataset. It is a preferable method over load_dataset for memory-effiency. (more details, please see [xarray.open_dataset](https://docs.xarray.dev/en/stable/generated/xarray.open_dataset.html))\n", + "\n", + "See the following example in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a440a329", + "metadata": {}, + "outputs": [], + "source": [ + "# open single file\n", + "full_path = latest_file\n", + "print(full_path)\n", + "ds_single = xr.open_dataset(full_path)\n", + "ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0143a3d", + "metadata": {}, + "outputs": [], + "source": [ + "print(ds_single)\n", + "print(type(ds_single))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c0f8939", + "metadata": {}, + "outputs": [], + "source": [ + "# open multiple files\n", + "n_files = 3\n", + "full_paths = [os.path.join(datastream_dir, f_path) for f_path in file_sorted_reverse[:n_files]]\n", + "print(full_paths)\n", + "try: # Note: sometimes multiple files cannot be merged, so we used try except here.\n", + " ds_mutiple = xr.open_mfdataset(full_paths)\n", + " # ds_mutiple\n", + " print(type(ds_mutiple))\n", + " print(ds_mutiple)\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "3fa59943", + "metadata": {}, + "source": [ + "## Explore the data" + ] + }, + { + "cell_type": "markdown", + "id": "571f69b9", + "metadata": {}, + "source": [ + "### NetCDF Data structure \n", + "\n", + "\n", + "(If you are confident with NetCDF basics and xarray essentials, feel free to skip this session.)\n", + "Before we dive into data exploration, there are some eseential concpets we should be familiar with (shown below): \n", + "* Dataset\n", + "* Data array\n", + "* Variable\n", + "* Dimenssion\n", + "* Coordinate\n", + "* Data Type\n", + "* Meta Data (Attributes)\n", + "\n", + "We will not go into details about NetCDF basics and here are some references you might find helpful\n", + "* [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#)\n", + "* [Network Common Data Form (NetCDF)](https://www.unidata.ucar.edu/software/netcdf/) and [A Brief History of (netCDF) Time](https://www.unidata.ucar.edu/software/netcdf/time/recs.html)\n", + "\n", + "\n", + "\n", + "### Xarray essentials\n", + "Earlier we introduced the xarray pacakge and used `open_dataset` and `open_mfdataset` to retrieve NetCDF data file as an xarray Dataset object. Recall that you can review the data in a notebook by using `print(ds)` or `ds`. \n", + "\n", + "Using xarray to retrieve the aforementioned NetCDF basics is straightforwared. In general,\n", + "* Dataset: `ds`\n", + "* Data array: `ds.variables`\n", + "* Variable: `ds.ds.variables`\n", + "* Dimenssion: `ds.dims`\n", + "* Coordinate: `ds.coords`\n", + "* Data Type: `type`\n", + "* Meta Data (Attributes)\n", + "\n", + "Also, here are some references if you are new to xarray\n", + "* [Xarray in 45 minutes](https://tutorial.xarray.dev/overview/xarray-in-45-min.html)\n", + "* [Handling NetCDF Files using XArray for Absolute Beginners](https://towardsdatascience.com/handling-netcdf-files-using-xarray-for-absolute-beginners-111a8ab4463f)\n", + "\n", + "Try the following commands in action\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b3eac323", + "metadata": {}, + "outputs": [], + "source": [ + "ds = ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77ecf85d", + "metadata": {}, + "outputs": [], + "source": [ + "# Dataset \n", + "ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25e7de09", + "metadata": {}, + "outputs": [], + "source": [ + "# Data array, Variable\n", + "# Note: the info can be overwhelming. Do not attempt to grasp everything at the first glance.\n", + "ds.variables" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c41b67e", + "metadata": {}, + "outputs": [], + "source": [ + "# dimenssions\n", + "ds.dims" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "156f1dfc", + "metadata": {}, + "outputs": [], + "source": [ + "# coordinates\n", + "ds.coords" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "277d6064", + "metadata": {}, + "outputs": [], + "source": [ + "# Meta Data (Attributes)\n", + "ds.attrs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d334681f", + "metadata": {}, + "outputs": [], + "source": [ + "# type\n", + "print(type(ds))\n", + "print(type(ds.variables))\n", + "print(type(ds.dims))\n", + "print(type(ds.coords))\n", + "print(type(ds.attrs))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "643399d6", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: retrieve variable names only\n", + "list(ds.variables)" + ] + }, + { + "cell_type": "markdown", + "id": "1cf36878", + "metadata": {}, + "source": [ + "#### Discussion: variable vs. coordinates vs. dimenssions. \n", + "From [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#):\n", + "\n", + "* Dimession: A dimension may be used to represent a real physical dimension, for example, time, latitude, longitude, or height. A dimension might also be used to index other quantities, for example station or model-run-number.\n", + "A netCDF dimension has both a name and a length. A dimension length is an arbitrary positive integer, except that one dimension in a netCDF dataset can have the length UNLIMITED.\n", + "\n", + "* Variables: Variables are used to store the bulk of the data in a netCDF dataset. A variable represents an array of values of the same type. A scalar value is treated as a 0-dimensional array. A variable has a name, a data type, and a shape described by its list of dimensions specified when the variable is created. A variable may also have associated attributes, which may be added, deleted or changed after the variable is created.\n", + "\n", + "* Coordinate (Variables): It is legal for a variable to have the same name as a dimension. Such variables have no special meaning to the netCDF library. However there is a convention that such variables should be treated in a special way by software using this library.\n", + "A variable with the same name as a dimension is called a coordinate variable. It typically defines a physical coordinate corresponding to that dimension.\n", + "\n", + "\n", + "Tips: By definition, coordinates and dimenssions are also variable. For example ds.time is a coordinate but it is also a special variable. But in practice, when we talk about variable, it implies regular/non-coordinate variable. We will use this convention for the remaining notebook. " + ] + }, + { + "cell_type": "markdown", + "id": "d47120f9", + "metadata": {}, + "source": [ + "### Xarray Variable\n", + "\n", + "\n", + "We can use `ds.variables` to access varialbes of a dataset. But many times viewing print-out the whole datasets can be overwhemling. Instead, we would work on the individual variable (also called Xarray data array.) using the `ds[var_name]` syntax, where `var_name` is the name of the variable. See the following examples in action.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5a57e136", + "metadata": {}, + "outputs": [], + "source": [ + "print(type(ds[\"time\"]))\n", + "ds[\"time\"]" + ] + }, + { + "cell_type": "markdown", + "id": "51ec643e", + "metadata": {}, + "source": [ + "#### Variable properties\n", + "For individual varible, the following properties are mostly used with the assocaited xarray method to retrieve them. (assuming var = ds[var_name])\n", + "* name: `var.name`\n", + "* data content: `var.data`\n", + "* attributes: `var.attrs`\n", + "* dimenstions: `var.dims`\n", + "* data type: `var.data.dtype`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a632a525", + "metadata": {}, + "outputs": [], + "source": [ + "var_name = \"time\"\n", + "var = ds[var_name]\n", + "\n", + "print(\"var.name: \\n\", var.name, \"\\n\")\n", + "print(\"var.data: \\n\", var.data, \"\\n\")\n", + "print(\"var.attrs: \\n\", var.attrs, \"\\n\")\n", + "print(\"var.dims: \\n\", var.dims, \"\\n\")\n", + "print(\"var.data.dtype: \\n\", var.data.dtype, \"\\n\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b256b07f", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: indivdual variable info table (collect and display variable info as a tabular format)\n", + "df_info = pd.DataFrame()\n", + "df_info[\"var_name\"] = list(ds.variables)\n", + "df_info[\"dims\"] = df_info.var_name.apply(lambda x: ds[x].dims)\n", + "df_info[\"is_dim\"] = df_info.var_name.apply(lambda x: x in ds.dims)\n", + "df_info[\"n_dim\"] = df_info.var_name.apply(lambda x: len(ds[x].dims))\n", + "df_info[\"attrs\"] = df_info.var_name.apply(lambda x: ds[x].attrs)\n", + "df_info[\"dtype\"] = df_info.var_name.apply(lambda x: ds[x].data.dtype)\n", + "df_info" + ] + }, + { + "cell_type": "markdown", + "id": "e6a36b96", + "metadata": {}, + "source": [ + "### Data cleaning/Preprocessing (skipped)\n", + "Data cleaning and preprocessing is an important stage in the the data analysis pipeline. However it will be out of the scope of this notebook. For more information about data cleaning and preprocessing basics, here are some references. \n", + "* [Xarray Fundamentals](https://earth-env-data-science.github.io/lectures/xarray/xarray.html)\n", + "* [Xarray Tutorial — Pangeo Gallery documentation](http://gallery.pangeo.io/repos/pangeo-data/pangeo-tutorial-gallery/xarray.html)\n", + "* [Pythonic Data Cleaning With pandas and NumPy](https://realpython.com/python-data-cleaning-numpy-pandas/)\n", + "* [Pandas - Cleaning Data](https://www.w3schools.com/python/pandas/pandas_cleaning.asp)" + ] + }, + { + "cell_type": "markdown", + "id": "ae6e1a69", + "metadata": {}, + "source": [ + "### Plotting\n", + "Data visualization (or plotting) is another important data analysis topic and deserves its own discussion. Here in this notebook we will only demonstrate basic tool for simple data visualization tasks.\n", + "\n", + "Here are some reference you might find useful:\n", + "* xarray plotting: https://docs.xarray.dev/en/stable/user-guide/plotting.html\n", + "* Atmospheric Community Toolkit (ACT): https://arm-doe.github.io/ACT/index.html\n", + "\n", + "Note: this notebook is auto-generated using a template. It uses a general idea to select variable(s) to plot and is not customized to each indivdual notebook. Feel free to change the variables in intrrests, especially certain figure is failed to plot." + ] + }, + { + "cell_type": "markdown", + "id": "7e417fb2", + "metadata": {}, + "source": [ + "#### 1-dimenssional basic time series plot\n", + "\n", + "For the following plot we would like to find variables such that\n", + "* it has one and only one dimession\n", + "* \"time\" is its coordinate variable\n", + "* it is not a dimenssion itself,\n", + "* it is not a special variable with substrings within [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "\n", + "Please see the following example in action. (For more details about pandas filtering, please see the following references.)\n", + "* [pandas: multiple conditions while indexing data frame](https://stackoverflow.com/questions/22591174/pandas-multiple-conditions-while-indexing-data-frame-unexpected-behavior)\n", + "* [pandas.DataFrame.apply](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.apply.html)\n", + "* [How to test if a string contains one of the substrings in a list...](https://stackoverflow.com/questions/26577516/how-to-test-if-a-string-contains-one-of-the-substrings-in-a-list-in-pandas" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30edac2d", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==1, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter = df_info[(df_info.n_dim == 1) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4777a659", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_1d = df_filter.var_name.values[0]\n", + " var_1d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb5ae985", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " ds[var_1d].plot()\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "9e41066e", + "metadata": {}, + "source": [ + "#### 2-dimenssional basic plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09bd4adc", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==2, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_2 = df_info[(df_info.n_dim == 2) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2cdea1a9", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_2d = df_filter_2.var_name.values[0]\n", + " var_2d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5f48879", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " print(ds[var_2d].dims)\n", + " # ds[var_2d].plot()\n", + "\n", + " # conventionally, use \"time\" as x-axis\n", + " ds[var_2d].plot(x=\"time\")\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "7554b2a6", + "metadata": {}, + "source": [ + "#### qc-plotting (optional)\n", + "\n", + "Note: act qc plotting has more strict requirement, one of them is the associated qc variable need to have \"flag_masks\" attributes. Which is added by using ds = act.io.armfiles.read_netcdf(files_list), then ds.clean.cleanup()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71c2096f", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " ds_act = act.io.armfiles.read_netcdf(full_path)\n", + " print(type(ds_act))\n", + " ds_act.clean.cleanup()\n", + "\n", + " # or \n", + " # ds.clean.cleanup()\n", + "except Exception as e:\n", + " print(\"ERROR\", e)\n", + " ds_act = ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "844f8505", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter valid variables for ACT qc plotting\n", + "condition = (\"qc_\" + df_info.var_name).apply(lambda x: ds_act[x].attrs.get(\"flag_masks\") is not None \n", + " if x in list(ds.variables) else False)\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_3 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings))) &\n", + " condition\n", + " ]\n", + "df_filter_3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd38cc6", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "try:\n", + " qc_variable = df_filter_3.var_name.values[0]\n", + " print(qc_variable)\n", + "except Exception as e:\n", + " print(e)\n", + " \n", + "try:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + " qc_ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "d76e4d27", + "metadata": {}, + "source": [ + "#### bonus: choose variables to plot from a dropdown menu " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb733804", + "metadata": {}, + "outputs": [], + "source": [ + "# Valid variables filtering\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_4 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82d5c20d", + "metadata": {}, + "outputs": [], + "source": [ + "# example 1: using xarray plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2fc9aaa4", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# fig, ax = plt.subplots(figsize=(10, 4))\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "# fig.clear() # Remove old lines from plot and plot new one\n", + "# if len(ds[var].dims)==2:\n", + "# ds[var].plot(x=\"time\", add_colorbar=False)\n", + "# else:\n", + "# ds[var].plot()\n", + "# plt.grid()\n", + "# plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fabdd802", + "metadata": {}, + "outputs": [], + "source": [ + "# example 2: using act plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "962b4186", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "\n", + "\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "\n", + "# i_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + "# i_display.add_subplots((1,), figsize=(10, 4))\n", + "# ax = i_display.plot(var, subplot_index=(0,), set_title=f\"{var} ({ds_act[var].attrs['long_name']})\",)\n", + "\n", + "# ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "# ax.grid()\n", + "# plt.show()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/RADFLUXANAL/radflux1long.c1.ipynb b/VAPs/quicklook/RADFLUXANAL/radflux1long.c1.ipynb new file mode 100644 index 00000000..c709b441 --- /dev/null +++ b/VAPs/quicklook/RADFLUXANAL/radflux1long.c1.ipynb @@ -0,0 +1,339 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# RADFLUX1LONG.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/radfluxanal) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'radflux1long'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2021-06-12', 'facility': 'M1', 'site': 'oli', 'start_date': '2020-10-16'}, {'end_date': '2023-09-14', 'facility': 'C1', 'site': 'nsa', 'start_date': '2021-07-04'}, {'end_date': '2023-09-28', 'facility': 'E36', 'site': 'sgp', 'start_date': '2020-06-26'}, {'end_date': '2023-10-31', 'facility': 'E37', 'site': 'sgp', 'start_date': '2020-07-01'}, {'end_date': '2021-05-29', 'facility': 'E38', 'site': 'sgp', 'start_date': '2020-06-25'}, {'end_date': '2023-09-28', 'facility': 'E11', 'site': 'sgp', 'start_date': '2020-06-08'}, {'end_date': '2023-10-30', 'facility': 'E12', 'site': 'sgp', 'start_date': '2020-06-07'}, {'end_date': '2023-10-26', 'facility': 'E13', 'site': 'sgp', 'start_date': '2020-06-03'}, {'end_date': '2023-09-28', 'facility': 'E15', 'site': 'sgp', 'start_date': '2021-07-12'}, {'end_date': '2021-09-20', 'facility': 'E31', 'site': 'sgp', 'start_date': '2020-06-08'}, {'end_date': '2023-10-31', 'facility': 'E32', 'site': 'sgp', 'start_date': '2021-07-03'}, {'end_date': '2023-10-31', 'facility': 'E33', 'site': 'sgp', 'start_date': '2020-06-11'}, {'end_date': '2023-09-28', 'facility': 'E34', 'site': 'sgp', 'start_date': '2020-06-18'}, {'end_date': '2023-09-28', 'facility': 'E35', 'site': 'sgp', 'start_date': '2019-06-24'}, {'end_date': '2023-10-30', 'facility': 'E39', 'site': 'sgp', 'start_date': '2020-09-07'}, {'end_date': '2023-09-28', 'facility': 'E40', 'site': 'sgp', 'start_date': '2020-09-07'}, {'end_date': '2023-08-02', 'facility': 'E41', 'site': 'sgp', 'start_date': '2021-07-31'}, {'end_date': '2023-09-28', 'facility': 'E9', 'site': 'sgp', 'start_date': '2021-07-04'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'E36' )\n", + "\n", + "date_start = '2023-09-25'\n", + "date_end = '2023-09-27'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['downwelling_shortwave', 'clearsky_downwelling_shortwave', 'downwelling_longwave']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'downwelling_shortwave'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'downwelling_shortwave'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/RADFLUXANAL/radflux1long.c2.ipynb b/VAPs/quicklook/RADFLUXANAL/radflux1long.c2.ipynb new file mode 100644 index 00000000..0ff95eed --- /dev/null +++ b/VAPs/quicklook/RADFLUXANAL/radflux1long.c2.ipynb @@ -0,0 +1,339 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# RADFLUX1LONG.C2 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/radfluxanal) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'radflux1long'\n", + "DATA_LEVEL = 'c2'\n", + "LOCATIONS = [{'end_date': '2016-12-20', 'facility': 'M1', 'site': 'awr', 'start_date': '2015-11-24'}, {'end_date': '2017-11-01', 'facility': 'M1', 'site': 'asi', 'start_date': '2016-05-12'}, {'end_date': '2020-05-31', 'facility': 'M1', 'site': 'anx', 'start_date': '2019-12-01'}, {'end_date': '2021-08-20', 'facility': 'C1', 'site': 'ena', 'start_date': '2013-10-03'}, {'end_date': '2022-09-30', 'facility': 'M1', 'site': 'hou', 'start_date': '2021-08-22'}, {'end_date': '2007-12-23', 'facility': 'M1', 'site': 'fkb', 'start_date': '2007-03-25'}, {'end_date': '2015-12-01', 'facility': 'M1', 'site': 'mao', 'start_date': '2014-01-01'}, {'end_date': '2018-03-13', 'facility': 'S1', 'site': 'mcq', 'start_date': '2016-04-10'}, {'end_date': '2008-12-08', 'facility': 'M1', 'site': 'hfe', 'start_date': '2008-11-19'}, {'end_date': '2019-04-28', 'facility': 'M1', 'site': 'cor', 'start_date': '2018-09-23'}, {'end_date': '2012-01-11', 'facility': 'M1', 'site': 'gan', 'start_date': '2011-11-07'}, {'end_date': '2011-01-06', 'facility': 'M1', 'site': 'grw', 'start_date': '2009-04-23'}, {'end_date': '2005-09-10', 'facility': 'M1', 'site': 'pye', 'start_date': '2005-02-22'}, {'end_date': '2019-07-12', 'facility': 'M1', 'site': 'oli', 'start_date': '2014-02-20'}, {'end_date': '2021-07-04', 'facility': 'C1', 'site': 'nsa', 'start_date': '2003-09-22'}, {'end_date': '2010-10-14', 'facility': 'C2', 'site': 'nsa', 'start_date': '2005-02-18'}, {'end_date': '2006-12-26', 'facility': 'M1', 'site': 'nim', 'start_date': '2005-12-14'}, {'end_date': '2021-07-25', 'facility': 'E35', 'site': 'sgp', 'start_date': '2011-10-06'}, {'end_date': '2021-07-27', 'facility': 'E36', 'site': 'sgp', 'start_date': '2011-09-28'}, {'end_date': '2021-07-27', 'facility': 'E37', 'site': 'sgp', 'start_date': '2011-09-28'}, {'end_date': '2020-06-25', 'facility': 'E38', 'site': 'sgp', 'start_date': '2011-08-25'}, {'end_date': '2020-05-23', 'facility': 'C1', 'site': 'sgp', 'start_date': '1997-03-25'}, {'end_date': '2011-10-16', 'facility': 'E10', 'site': 'sgp', 'start_date': '1995-08-10'}, {'end_date': '2021-07-05', 'facility': 'E11', 'site': 'sgp', 'start_date': '1995-09-26'}, {'end_date': '2021-07-03', 'facility': 'E12', 'site': 'sgp', 'start_date': '1996-01-21'}, {'end_date': '2021-07-12', 'facility': 'E13', 'site': 'sgp', 'start_date': '1994-01-07'}, {'end_date': '2021-07-12', 'facility': 'E15', 'site': 'sgp', 'start_date': '1994-03-31'}, {'end_date': '2011-11-10', 'facility': 'E16', 'site': 'sgp', 'start_date': '1995-06-12'}, {'end_date': '2009-11-07', 'facility': 'E18', 'site': 'sgp', 'start_date': '1996-06-20'}, {'end_date': '2011-05-21', 'facility': 'E19', 'site': 'sgp', 'start_date': '1998-07-19'}, {'end_date': '2009-05-13', 'facility': 'E1', 'site': 'sgp', 'start_date': '1995-11-15'}, {'end_date': '2011-11-15', 'facility': 'E20', 'site': 'sgp', 'start_date': '1995-04-01'}, {'end_date': '2019-04-27', 'facility': 'E21', 'site': 'sgp', 'start_date': '1999-09-13'}, {'end_date': '2009-11-30', 'facility': 'E22', 'site': 'sgp', 'start_date': '1995-06-12'}, {'end_date': '2009-11-07', 'facility': 'E24', 'site': 'sgp', 'start_date': '1995-11-08'}, {'end_date': '2002-04-03', 'facility': 'E25', 'site': 'sgp', 'start_date': '1997-11-18'}, {'end_date': '2009-07-15', 'facility': 'E27', 'site': 'sgp', 'start_date': '2003-05-29'}, {'end_date': '2009-10-19', 'facility': 'E2', 'site': 'sgp', 'start_date': '1996-03-25'}, {'end_date': '2021-07-12', 'facility': 'E31', 'site': 'sgp', 'start_date': '2011-10-13'}, {'end_date': '2021-07-03', 'facility': 'E32', 'site': 'sgp', 'start_date': '2012-02-05'}, {'end_date': '2021-07-31', 'facility': 'E33', 'site': 'sgp', 'start_date': '2011-08-25'}, {'end_date': '2021-07-29', 'facility': 'E34', 'site': 'sgp', 'start_date': '2011-09-04'}, {'end_date': '2021-07-12', 'facility': 'E39', 'site': 'sgp', 'start_date': '2015-10-06'}, {'end_date': '2009-10-24', 'facility': 'E3', 'site': 'sgp', 'start_date': '1996-03-07'}, {'end_date': '2021-07-23', 'facility': 'E40', 'site': 'sgp', 'start_date': '2015-10-08'}, {'end_date': '2021-07-31', 'facility': 'E41', 'site': 'sgp', 'start_date': '2016-04-21'}, {'end_date': '2011-09-25', 'facility': 'E4', 'site': 'sgp', 'start_date': '1996-03-07'}, {'end_date': '2009-11-01', 'facility': 'E5', 'site': 'sgp', 'start_date': '1996-06-17'}, {'end_date': '2011-10-16', 'facility': 'E6', 'site': 'sgp', 'start_date': '1996-03-07'}, {'end_date': '2011-11-13', 'facility': 'E7', 'site': 'sgp', 'start_date': '1995-10-20'}, {'end_date': '2009-11-05', 'facility': 'E8', 'site': 'sgp', 'start_date': '1995-09-29'}, {'end_date': '2021-07-03', 'facility': 'E9', 'site': 'sgp', 'start_date': '1994-01-19'}, {'end_date': '2014-06-29', 'facility': 'C1', 'site': 'twp', 'start_date': '1996-10-14'}, {'end_date': '2013-09-08', 'facility': 'C2', 'site': 'twp', 'start_date': '1998-10-30'}, {'end_date': '2015-01-06', 'facility': 'C3', 'site': 'twp', 'start_date': '2002-03-12'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'E35' )\n", + "\n", + "date_start = '2021-07-22'\n", + "date_end = '2021-07-24'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['downwelling_shortwave', 'clearsky_downwelling_shortwave', 'downwelling_longwave']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'downwelling_shortwave'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'downwelling_shortwave'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/RADFLUXANAL/radfluxbrs1long.c2.ipynb b/VAPs/quicklook/RADFLUXANAL/radfluxbrs1long.c2.ipynb new file mode 100644 index 00000000..98d71f59 --- /dev/null +++ b/VAPs/quicklook/RADFLUXANAL/radfluxbrs1long.c2.ipynb @@ -0,0 +1,339 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# RADFLUXBRS1LONG.C2 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/radfluxanal) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'radfluxbrs1long'\n", + "DATA_LEVEL = 'c2'\n", + "LOCATIONS = [{'end_date': '2021-07-12', 'facility': 'C1', 'site': 'sgp', 'start_date': '1997-03-21'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2021-07-09'\n", + "date_end = '2021-07-11'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['downwelling_shortwave', 'clearsky_downwelling_shortwave', 'downwelling_longwave']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'downwelling_shortwave'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'downwelling_shortwave'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/RIPBE/30ripbe1mcfarlane.c1.ipynb b/VAPs/quicklook/RIPBE/30ripbe1mcfarlane.c1.ipynb new file mode 100644 index 00000000..3b769c15 --- /dev/null +++ b/VAPs/quicklook/RIPBE/30ripbe1mcfarlane.c1.ipynb @@ -0,0 +1,339 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# 30RIPBE1MCFARLANE.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/ripbe) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = '30ripbe1mcfarlane'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2007-07-31', 'facility': 'C1', 'site': 'sgp', 'start_date': '2002-03-01'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2007-07-29'\n", + "date_end = '2007-07-31'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['solar_zenith', 'solar_distance_factor', 'clear_sky_frac']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'clear_sky_frac'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'solar_zenith'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/RIPBE/RIPBE_tutorial.ipynb b/VAPs/quicklook/RIPBE/RIPBE_tutorial.ipynb new file mode 100644 index 00000000..dffde185 --- /dev/null +++ b/VAPs/quicklook/RIPBE/RIPBE_tutorial.ipynb @@ -0,0 +1,838 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# 30RIPBE1MCFARLANE.C1 Notebook\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/ripbe) for more information about this vap." + ] + }, + { + "cell_type": "markdown", + "id": "97097763", + "metadata": {}, + "source": [ + "In this notebook, we demonstrate the workflow to explore ARM vap data (using 30ripbe1mcfarlane as an example.) Value-added products (VAPs) are higher-order data products that have been analyzed and processed to ease scientists’ use of ARM data in atmospheric research and global climate models. \n", + "Here is the main content we will cover." + ] + }, + { + "cell_type": "markdown", + "id": "eddec40f", + "metadata": {}, + "source": [ + "# Table of Content\n", + "## Access the data\n", + "* How to retrieve the data\n", + "* Data path and file name conventions\n", + "* Load data\n", + "## Explore the data\n", + "* NetCDF Data structure\n", + "* Xarray essentials\n", + "* Xarray Variable\n", + "## Plot the data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "import random\n", + "\n", + "import glob\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "2372d5be", + "metadata": {}, + "source": [ + "## Access the data" + ] + }, + { + "cell_type": "markdown", + "id": "5b0d9684", + "metadata": {}, + "source": [ + "### How to retrieve the data\n", + "We assume the path \"/data/archive\" is available where you are running this notebook. You can use `os.path.exists(\"/data/archive\")` to verify if the path exists at your machine.\n", + "\n", + "\n", + "### Data path and file name conventions\n", + "There several common terminologies regarding ARM data, for example, data-stream-name, data-level, site, facility, instrument, etc. (For more details, please see [ARM Data File Standards Version 1.2](https://www.google.com/search?q=arm+datastream+facility+cite+definition&rlz=1C1GCEJ_enUS1029US1029&ei=hb41ZICrBPukqtsPvMODIA&ved=0ahUKEwjAgeiS0aL-AhV7kmoFHbzhAAQQ4dUDCBA&uact=5&oq=arm+datastream+facility+cite+definition&gs_lcp=Cgxnd3Mtd2l6LXNlcnAQAzIFCAAQogQyBQgAEKIEMgUIABCiBDIFCAAQogQyBQgAEKIEOgoIABBHENYEELADOgoIIRCgARDDBBAKSgQIQRgAUJ4DWIoOYO0PaAFwAXgAgAGMAYgBuQmSAQMyLjmYAQCgAQHIAQjAAQE&sclient=gws-wiz-serp).) \n", + "\n", + "For example, this notebook is called `30ripbe1mcfarlane.c1`, where `30ripbe1mcfarlane` is the \"datastream name\", and the `{process.ds_class_level}}` is the \"data level\".\n", + "\n", + "This datastream also contains site `sgp` and facility `C1`. (Note: individual datastream might have multiple site-facility pairs.)\n", + "In such a case, the data of this data-stream is stored at `/data/archive/sgp/sgp30ripbe1mcfarlaneC1.c1`, which is in the format of `//.`. We can use the following method to assign the data-stream directory `datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )`\n", + "\n", + "The data files under datastream_dir also follows naming conventions. But once reach the datastream_dir level, the most import file naming convention to differentiate the files is \"yyyyMMdd.hhmmss\", which comes handy to filter out files based on datetime. For example, we can use `glob.glob(f'{datastream_dir}/*.200709*.*')` to filter files in 2007 September.\n", + "\n", + "(Note: refer to the https://adc.arm.gov/discovery/#/ and https://adc.arm.gov/solr8/metadata/select API to explore ARM datastream and assoicated available sites and facilities\n", + "\n", + "Please see the following examples in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d7e9eb85", + "metadata": {}, + "outputs": [], + "source": [ + "# Verify if DATA_DIR path exists\n", + "DATA_DIR = \"/data/archive\"\n", + "os.path.exists(DATA_DIR)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "586993fd", + "metadata": {}, + "outputs": [], + "source": [ + "# Speicify datastream_dir following the path conventions and check its existence\n", + "DATASTREAM_NAME = \"30ripbe1mcfarlane\"\n", + "DATA_LEVEL = \"c1\"\n", + "site = \"sgp\"\n", + "facility = \"C1\"\n", + "datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )\n", + "print(datastream_dir)\n", + "print(os.path.exists(datastream_dir))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0742f7c1", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: list 5 (random) files under datastream_dir\n", + "files = os.listdir(datastream_dir)\n", + "files[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39b98a36", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: get most recent file\n", + "list_of_files = glob.glob(f\"{datastream_dir}/*\") # * means all if need specific format then *.csv\n", + "latest_file = max(list_of_files, key=os.path.getctime)\n", + "latest_file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "902d514e", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus sort datastream files based on datetime\n", + "files = os.listdir(datastream_dir)\n", + "file_sorted = files.copy()\n", + "file_sorted.sort() \n", + "print(file_sorted[:5])\n", + "\n", + "# to reverse\n", + "file_sorted_reverse = files.copy() \n", + "file_sorted_reverse.sort(reverse=True)\n", + "print(file_sorted_reverse[:5])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec5923b2", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: pattern matching\n", + "# filter the 200709** files under datastream_dir\n", + "files_filter = glob.glob(f'{datastream_dir}/*.200709*.*')\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c4a4aa18", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: use armnotebook_utils.file_filter (TODO) to filter files based on datastream info \n" + ] + }, + { + "cell_type": "markdown", + "id": "cfeb9efc", + "metadata": {}, + "source": [ + "### Load data\n", + "The arm data is stored in [NetCDF](https://en.wikipedia.org/wiki/NetCDF#:~:text=%22NetCDF%20(network%20Common%20Data%20Form,format%20for%20representing%20scientific%20data.) format. We can use xarray's `open_dataset` method to load single file, or `open_mfdataset` to open multiple files. (Note: the latter will still return a single xarray dataset object by combining multiple files.)\n", + "\n", + "Note: open_dataset keeps the file handle open and lazy loads its contents. All parameters are passed directly to open_dataset. It is a preferable method over load_dataset for memory-effiency. (more details, please see [xarray.open_dataset](https://docs.xarray.dev/en/stable/generated/xarray.open_dataset.html))\n", + "\n", + "See the following example in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a440a329", + "metadata": {}, + "outputs": [], + "source": [ + "# open single file\n", + "full_path = latest_file\n", + "print(full_path)\n", + "ds_single = xr.open_dataset(full_path)\n", + "ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0143a3d", + "metadata": {}, + "outputs": [], + "source": [ + "print(ds_single)\n", + "print(type(ds_single))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c0f8939", + "metadata": {}, + "outputs": [], + "source": [ + "# open multiple files\n", + "n_files = 3\n", + "full_paths = [os.path.join(datastream_dir, f_path) for f_path in file_sorted_reverse[:n_files]]\n", + "print(full_paths)\n", + "try: # Note: sometimes multiple files cannot be merged, so we used try except here.\n", + " ds_mutiple = xr.open_mfdataset(full_paths)\n", + " # ds_mutiple\n", + " print(type(ds_mutiple))\n", + " print(ds_mutiple)\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "3fa59943", + "metadata": {}, + "source": [ + "## Explore the data" + ] + }, + { + "cell_type": "markdown", + "id": "571f69b9", + "metadata": {}, + "source": [ + "### NetCDF Data structure \n", + "\n", + "\n", + "(If you are confident with NetCDF basics and xarray essentials, feel free to skip this session.)\n", + "Before we dive into data exploration, there are some eseential concpets we should be familiar with (shown below): \n", + "* Dataset\n", + "* Data array\n", + "* Variable\n", + "* Dimenssion\n", + "* Coordinate\n", + "* Data Type\n", + "* Meta Data (Attributes)\n", + "\n", + "We will not go into details about NetCDF basics and here are some references you might find helpful\n", + "* [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#)\n", + "* [Network Common Data Form (NetCDF)](https://www.unidata.ucar.edu/software/netcdf/) and [A Brief History of (netCDF) Time](https://www.unidata.ucar.edu/software/netcdf/time/recs.html)\n", + "\n", + "\n", + "\n", + "### Xarray essentials\n", + "Earlier we introduced the xarray pacakge and used `open_dataset` and `open_mfdataset` to retrieve NetCDF data file as an xarray Dataset object. Recall that you can review the data in a notebook by using `print(ds)` or `ds`. \n", + "\n", + "Using xarray to retrieve the aforementioned NetCDF basics is straightforwared. In general,\n", + "* Dataset: `ds`\n", + "* Data array: `ds.variables`\n", + "* Variable: `ds.ds.variables`\n", + "* Dimenssion: `ds.dims`\n", + "* Coordinate: `ds.coords`\n", + "* Data Type: `type`\n", + "* Meta Data (Attributes)\n", + "\n", + "Also, here are some references if you are new to xarray\n", + "* [Xarray in 45 minutes](https://tutorial.xarray.dev/overview/xarray-in-45-min.html)\n", + "* [Handling NetCDF Files using XArray for Absolute Beginners](https://towardsdatascience.com/handling-netcdf-files-using-xarray-for-absolute-beginners-111a8ab4463f)\n", + "\n", + "Try the following commands in action\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b3eac323", + "metadata": {}, + "outputs": [], + "source": [ + "ds = ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77ecf85d", + "metadata": {}, + "outputs": [], + "source": [ + "# Dataset \n", + "ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25e7de09", + "metadata": {}, + "outputs": [], + "source": [ + "# Data array, Variable\n", + "# Note: the info can be overwhelming. Do not attempt to grasp everything at the first glance.\n", + "ds.variables" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c41b67e", + "metadata": {}, + "outputs": [], + "source": [ + "# dimenssions\n", + "ds.dims" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "156f1dfc", + "metadata": {}, + "outputs": [], + "source": [ + "# coordinates\n", + "ds.coords" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "277d6064", + "metadata": {}, + "outputs": [], + "source": [ + "# Meta Data (Attributes)\n", + "ds.attrs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d334681f", + "metadata": {}, + "outputs": [], + "source": [ + "# type\n", + "print(type(ds))\n", + "print(type(ds.variables))\n", + "print(type(ds.dims))\n", + "print(type(ds.coords))\n", + "print(type(ds.attrs))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "643399d6", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: retrieve variable names only\n", + "list(ds.variables)" + ] + }, + { + "cell_type": "markdown", + "id": "1cf36878", + "metadata": {}, + "source": [ + "#### Discussion: variable vs. coordinates vs. dimenssions. \n", + "From [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#):\n", + "\n", + "* Dimession: A dimension may be used to represent a real physical dimension, for example, time, latitude, longitude, or height. A dimension might also be used to index other quantities, for example station or model-run-number.\n", + "A netCDF dimension has both a name and a length. A dimension length is an arbitrary positive integer, except that one dimension in a netCDF dataset can have the length UNLIMITED.\n", + "\n", + "* Variables: Variables are used to store the bulk of the data in a netCDF dataset. A variable represents an array of values of the same type. A scalar value is treated as a 0-dimensional array. A variable has a name, a data type, and a shape described by its list of dimensions specified when the variable is created. A variable may also have associated attributes, which may be added, deleted or changed after the variable is created.\n", + "\n", + "* Coordinate (Variables): It is legal for a variable to have the same name as a dimension. Such variables have no special meaning to the netCDF library. However there is a convention that such variables should be treated in a special way by software using this library.\n", + "A variable with the same name as a dimension is called a coordinate variable. It typically defines a physical coordinate corresponding to that dimension.\n", + "\n", + "\n", + "Tips: By definition, coordinates and dimenssions are also variable. For example ds.time is a coordinate but it is also a special variable. But in practice, when we talk about variable, it implies regular/non-coordinate variable. We will use this convention for the remaining notebook. " + ] + }, + { + "cell_type": "markdown", + "id": "d47120f9", + "metadata": {}, + "source": [ + "### Xarray Variable\n", + "\n", + "\n", + "We can use `ds.variables` to access varialbes of a dataset. But many times viewing print-out the whole datasets can be overwhemling. Instead, we would work on the individual variable (also called Xarray data array.) using the `ds[var_name]` syntax, where `var_name` is the name of the variable. See the following examples in action.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5a57e136", + "metadata": {}, + "outputs": [], + "source": [ + "print(type(ds[\"time\"]))\n", + "ds[\"time\"]" + ] + }, + { + "cell_type": "markdown", + "id": "51ec643e", + "metadata": {}, + "source": [ + "#### Variable properties\n", + "For individual varible, the following properties are mostly used with the assocaited xarray method to retrieve them. (assuming var = ds[var_name])\n", + "* name: `var.name`\n", + "* data content: `var.data`\n", + "* attributes: `var.attrs`\n", + "* dimenstions: `var.dims`\n", + "* data type: `var.data.dtype`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a632a525", + "metadata": {}, + "outputs": [], + "source": [ + "var_name = \"time\"\n", + "var = ds[var_name]\n", + "\n", + "print(\"var.name: \\n\", var.name, \"\\n\")\n", + "print(\"var.data: \\n\", var.data, \"\\n\")\n", + "print(\"var.attrs: \\n\", var.attrs, \"\\n\")\n", + "print(\"var.dims: \\n\", var.dims, \"\\n\")\n", + "print(\"var.data.dtype: \\n\", var.data.dtype, \"\\n\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b256b07f", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: indivdual variable info table (collect and display variable info as a tabular format)\n", + "df_info = pd.DataFrame()\n", + "df_info[\"var_name\"] = list(ds.variables)\n", + "df_info[\"dims\"] = df_info.var_name.apply(lambda x: ds[x].dims)\n", + "df_info[\"is_dim\"] = df_info.var_name.apply(lambda x: x in ds.dims)\n", + "df_info[\"n_dim\"] = df_info.var_name.apply(lambda x: len(ds[x].dims))\n", + "df_info[\"attrs\"] = df_info.var_name.apply(lambda x: ds[x].attrs)\n", + "df_info[\"dtype\"] = df_info.var_name.apply(lambda x: ds[x].data.dtype)\n", + "df_info" + ] + }, + { + "cell_type": "markdown", + "id": "e6a36b96", + "metadata": {}, + "source": [ + "### Data cleaning/Preprocessing (skipped)\n", + "Data cleaning and preprocessing is an important stage in the the data analysis pipeline. However it will be out of the scope of this notebook. For more information about data cleaning and preprocessing basics, here are some references. \n", + "* [Xarray Fundamentals](https://earth-env-data-science.github.io/lectures/xarray/xarray.html)\n", + "* [Xarray Tutorial — Pangeo Gallery documentation](http://gallery.pangeo.io/repos/pangeo-data/pangeo-tutorial-gallery/xarray.html)\n", + "* [Pythonic Data Cleaning With pandas and NumPy](https://realpython.com/python-data-cleaning-numpy-pandas/)\n", + "* [Pandas - Cleaning Data](https://www.w3schools.com/python/pandas/pandas_cleaning.asp)" + ] + }, + { + "cell_type": "markdown", + "id": "ae6e1a69", + "metadata": {}, + "source": [ + "### Plotting\n", + "Data visualization (or plotting) is another important data analysis topic and deserves its own discussion. Here in this notebook we will only demonstrate basic tool for simple data visualization tasks.\n", + "\n", + "Here are some reference you might find useful:\n", + "* xarray plotting: https://docs.xarray.dev/en/stable/user-guide/plotting.html\n", + "* Atmospheric Community Toolkit (ACT): https://arm-doe.github.io/ACT/index.html\n", + "\n", + "Note: this notebook is auto-generated using a template. It uses a general idea to select variable(s) to plot and is not customized to each indivdual notebook. Feel free to change the variables in intrrests, especially certain figure is failed to plot." + ] + }, + { + "cell_type": "markdown", + "id": "7e417fb2", + "metadata": {}, + "source": [ + "#### 1-dimenssional basic time series plot\n", + "\n", + "For the following plot we would like to find variables such that\n", + "* it has one and only one dimession\n", + "* \"time\" is its coordinate variable\n", + "* it is not a dimenssion itself,\n", + "* it is not a special variable with substrings within [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "\n", + "Please see the following example in action. (For more details about pandas filtering, please see the following references.)\n", + "* [pandas: multiple conditions while indexing data frame](https://stackoverflow.com/questions/22591174/pandas-multiple-conditions-while-indexing-data-frame-unexpected-behavior)\n", + "* [pandas.DataFrame.apply](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.apply.html)\n", + "* [How to test if a string contains one of the substrings in a list...](https://stackoverflow.com/questions/26577516/how-to-test-if-a-string-contains-one-of-the-substrings-in-a-list-in-pandas" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30edac2d", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==1, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter = df_info[(df_info.n_dim == 1) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4777a659", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_1d = df_filter.var_name.values[0]\n", + " var_1d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb5ae985", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " ds[var_1d].plot()\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "9e41066e", + "metadata": {}, + "source": [ + "#### 2-dimenssional basic plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09bd4adc", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==2, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_2 = df_info[(df_info.n_dim == 2) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2cdea1a9", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_2d = df_filter_2.var_name.values[0]\n", + " var_2d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5f48879", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " print(ds[var_2d].dims)\n", + " # ds[var_2d].plot()\n", + "\n", + " # conventionally, use \"time\" as x-axis\n", + " ds[var_2d].plot(x=\"time\")\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "7554b2a6", + "metadata": {}, + "source": [ + "#### qc-plotting (optional)\n", + "\n", + "Note: act qc plotting has more strict requirement, one of them is the associated qc variable need to have \"flag_masks\" attributes. Which is added by using ds = act.io.armfiles.read_netcdf(files_list), then ds.clean.cleanup()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71c2096f", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " ds_act = act.io.armfiles.read_netcdf(full_path)\n", + " print(type(ds_act))\n", + " ds_act.clean.cleanup()\n", + "\n", + " # or \n", + " # ds.clean.cleanup()\n", + "except Exception as e:\n", + " print(\"ERROR\", e)\n", + " ds_act = ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "844f8505", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter valid variables for ACT qc plotting\n", + "condition = (\"qc_\" + df_info.var_name).apply(lambda x: ds_act[x].attrs.get(\"flag_masks\") is not None \n", + " if x in list(ds.variables) else False)\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_3 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings))) &\n", + " condition\n", + " ]\n", + "df_filter_3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd38cc6", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "try:\n", + " qc_variable = df_filter_3.var_name.values[0]\n", + " print(qc_variable)\n", + "except Exception as e:\n", + " print(e)\n", + " \n", + "try:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + " qc_ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "d76e4d27", + "metadata": {}, + "source": [ + "#### bonus: choose variables to plot from a dropdown menu " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb733804", + "metadata": {}, + "outputs": [], + "source": [ + "# Valid variables filtering\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_4 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82d5c20d", + "metadata": {}, + "outputs": [], + "source": [ + "# example 1: using xarray plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2fc9aaa4", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# fig, ax = plt.subplots(figsize=(10, 4))\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "# fig.clear() # Remove old lines from plot and plot new one\n", + "# if len(ds[var].dims)==2:\n", + "# ds[var].plot(x=\"time\", add_colorbar=False)\n", + "# else:\n", + "# ds[var].plot()\n", + "# plt.grid()\n", + "# plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fabdd802", + "metadata": {}, + "outputs": [], + "source": [ + "# example 2: using act plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "962b4186", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "\n", + "\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "\n", + "# i_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + "# i_display.add_subplots((1,), figsize=(10, 4))\n", + "# ax = i_display.plot(var, subplot_index=(0,), set_title=f\"{var} ({ds_act[var].attrs['long_name']})\",)\n", + "\n", + "# ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "# ax.grid()\n", + "# plt.show()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/RIPBE/ripbe1mcfarlane.c1.ipynb b/VAPs/quicklook/RIPBE/ripbe1mcfarlane.c1.ipynb new file mode 100644 index 00000000..912409e3 --- /dev/null +++ b/VAPs/quicklook/RIPBE/ripbe1mcfarlane.c1.ipynb @@ -0,0 +1,339 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# RIPBE1MCFARLANE.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/ripbe) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'ripbe1mcfarlane'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2011-06-05', 'facility': 'C1', 'site': 'sgp', 'start_date': '2002-03-01'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2011-06-03'\n", + "date_end = '2011-06-05'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['pressure_level', 'pressure_layer', 'temperature_level']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'clear_sky_flag'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'pressure_level'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/RLPROF/10rlprofbe1news.c1.ipynb b/VAPs/quicklook/RLPROF/10rlprofbe1news.c1.ipynb new file mode 100644 index 00000000..30df2e97 --- /dev/null +++ b/VAPs/quicklook/RLPROF/10rlprofbe1news.c1.ipynb @@ -0,0 +1,339 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# 10RLPROFBE1NEWS.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/rlprof) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = '10rlprofbe1news'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2015-09-21', 'facility': 'C1', 'site': 'sgp', 'start_date': '2004-10-01'}, {'end_date': '2015-01-01', 'facility': 'C3', 'site': 'twp', 'start_date': '2010-12-15'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2015-09-21'\n", + "date_end = '2015-09-21'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['asr', 'bscat', 'ext']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'asr'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'asr'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/RLPROF/RLPROF_tutorial.ipynb b/VAPs/quicklook/RLPROF/RLPROF_tutorial.ipynb new file mode 100644 index 00000000..cc7dac76 --- /dev/null +++ b/VAPs/quicklook/RLPROF/RLPROF_tutorial.ipynb @@ -0,0 +1,838 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# 10RLPROFBE1NEWS.C1 Notebook\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/rlprof) for more information about this vap." + ] + }, + { + "cell_type": "markdown", + "id": "97097763", + "metadata": {}, + "source": [ + "In this notebook, we demonstrate the workflow to explore ARM vap data (using 10rlprofbe1news as an example.) Value-added products (VAPs) are higher-order data products that have been analyzed and processed to ease scientists’ use of ARM data in atmospheric research and global climate models. \n", + "Here is the main content we will cover." + ] + }, + { + "cell_type": "markdown", + "id": "eddec40f", + "metadata": {}, + "source": [ + "# Table of Content\n", + "## Access the data\n", + "* How to retrieve the data\n", + "* Data path and file name conventions\n", + "* Load data\n", + "## Explore the data\n", + "* NetCDF Data structure\n", + "* Xarray essentials\n", + "* Xarray Variable\n", + "## Plot the data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "import random\n", + "\n", + "import glob\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "2372d5be", + "metadata": {}, + "source": [ + "## Access the data" + ] + }, + { + "cell_type": "markdown", + "id": "5b0d9684", + "metadata": {}, + "source": [ + "### How to retrieve the data\n", + "We assume the path \"/data/archive\" is available where you are running this notebook. You can use `os.path.exists(\"/data/archive\")` to verify if the path exists at your machine.\n", + "\n", + "\n", + "### Data path and file name conventions\n", + "There several common terminologies regarding ARM data, for example, data-stream-name, data-level, site, facility, instrument, etc. (For more details, please see [ARM Data File Standards Version 1.2](https://www.google.com/search?q=arm+datastream+facility+cite+definition&rlz=1C1GCEJ_enUS1029US1029&ei=hb41ZICrBPukqtsPvMODIA&ved=0ahUKEwjAgeiS0aL-AhV7kmoFHbzhAAQQ4dUDCBA&uact=5&oq=arm+datastream+facility+cite+definition&gs_lcp=Cgxnd3Mtd2l6LXNlcnAQAzIFCAAQogQyBQgAEKIEMgUIABCiBDIFCAAQogQyBQgAEKIEOgoIABBHENYEELADOgoIIRCgARDDBBAKSgQIQRgAUJ4DWIoOYO0PaAFwAXgAgAGMAYgBuQmSAQMyLjmYAQCgAQHIAQjAAQE&sclient=gws-wiz-serp).) \n", + "\n", + "For example, this notebook is called `10rlprofbe1news.c1`, where `10rlprofbe1news` is the \"datastream name\", and the `{process.ds_class_level}}` is the \"data level\".\n", + "\n", + "This datastream also contains site `sgp` and facility `C1`. (Note: individual datastream might have multiple site-facility pairs.)\n", + "In such a case, the data of this data-stream is stored at `/data/archive/sgp/sgp10rlprofbe1newsC1.c1`, which is in the format of `//.`. We can use the following method to assign the data-stream directory `datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )`\n", + "\n", + "The data files under datastream_dir also follows naming conventions. But once reach the datastream_dir level, the most import file naming convention to differentiate the files is \"yyyyMMdd.hhmmss\", which comes handy to filter out files based on datetime. For example, we can use `glob.glob(f'{datastream_dir}/*.200709*.*')` to filter files in 2007 September.\n", + "\n", + "(Note: refer to the https://adc.arm.gov/discovery/#/ and https://adc.arm.gov/solr8/metadata/select API to explore ARM datastream and assoicated available sites and facilities\n", + "\n", + "Please see the following examples in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d7e9eb85", + "metadata": {}, + "outputs": [], + "source": [ + "# Verify if DATA_DIR path exists\n", + "DATA_DIR = \"/data/archive\"\n", + "os.path.exists(DATA_DIR)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "586993fd", + "metadata": {}, + "outputs": [], + "source": [ + "# Speicify datastream_dir following the path conventions and check its existence\n", + "DATASTREAM_NAME = \"10rlprofbe1news\"\n", + "DATA_LEVEL = \"c1\"\n", + "site = \"sgp\"\n", + "facility = \"C1\"\n", + "datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )\n", + "print(datastream_dir)\n", + "print(os.path.exists(datastream_dir))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0742f7c1", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: list 5 (random) files under datastream_dir\n", + "files = os.listdir(datastream_dir)\n", + "files[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39b98a36", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: get most recent file\n", + "list_of_files = glob.glob(f\"{datastream_dir}/*\") # * means all if need specific format then *.csv\n", + "latest_file = max(list_of_files, key=os.path.getctime)\n", + "latest_file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "902d514e", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus sort datastream files based on datetime\n", + "files = os.listdir(datastream_dir)\n", + "file_sorted = files.copy()\n", + "file_sorted.sort() \n", + "print(file_sorted[:5])\n", + "\n", + "# to reverse\n", + "file_sorted_reverse = files.copy() \n", + "file_sorted_reverse.sort(reverse=True)\n", + "print(file_sorted_reverse[:5])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec5923b2", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: pattern matching\n", + "# filter the 200709** files under datastream_dir\n", + "files_filter = glob.glob(f'{datastream_dir}/*.200709*.*')\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c4a4aa18", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: use armnotebook_utils.file_filter (TODO) to filter files based on datastream info \n" + ] + }, + { + "cell_type": "markdown", + "id": "cfeb9efc", + "metadata": {}, + "source": [ + "### Load data\n", + "The arm data is stored in [NetCDF](https://en.wikipedia.org/wiki/NetCDF#:~:text=%22NetCDF%20(network%20Common%20Data%20Form,format%20for%20representing%20scientific%20data.) format. We can use xarray's `open_dataset` method to load single file, or `open_mfdataset` to open multiple files. (Note: the latter will still return a single xarray dataset object by combining multiple files.)\n", + "\n", + "Note: open_dataset keeps the file handle open and lazy loads its contents. All parameters are passed directly to open_dataset. It is a preferable method over load_dataset for memory-effiency. (more details, please see [xarray.open_dataset](https://docs.xarray.dev/en/stable/generated/xarray.open_dataset.html))\n", + "\n", + "See the following example in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a440a329", + "metadata": {}, + "outputs": [], + "source": [ + "# open single file\n", + "full_path = latest_file\n", + "print(full_path)\n", + "ds_single = xr.open_dataset(full_path)\n", + "ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0143a3d", + "metadata": {}, + "outputs": [], + "source": [ + "print(ds_single)\n", + "print(type(ds_single))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c0f8939", + "metadata": {}, + "outputs": [], + "source": [ + "# open multiple files\n", + "n_files = 3\n", + "full_paths = [os.path.join(datastream_dir, f_path) for f_path in file_sorted_reverse[:n_files]]\n", + "print(full_paths)\n", + "try: # Note: sometimes multiple files cannot be merged, so we used try except here.\n", + " ds_mutiple = xr.open_mfdataset(full_paths)\n", + " # ds_mutiple\n", + " print(type(ds_mutiple))\n", + " print(ds_mutiple)\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "3fa59943", + "metadata": {}, + "source": [ + "## Explore the data" + ] + }, + { + "cell_type": "markdown", + "id": "571f69b9", + "metadata": {}, + "source": [ + "### NetCDF Data structure \n", + "\n", + "\n", + "(If you are confident with NetCDF basics and xarray essentials, feel free to skip this session.)\n", + "Before we dive into data exploration, there are some eseential concpets we should be familiar with (shown below): \n", + "* Dataset\n", + "* Data array\n", + "* Variable\n", + "* Dimenssion\n", + "* Coordinate\n", + "* Data Type\n", + "* Meta Data (Attributes)\n", + "\n", + "We will not go into details about NetCDF basics and here are some references you might find helpful\n", + "* [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#)\n", + "* [Network Common Data Form (NetCDF)](https://www.unidata.ucar.edu/software/netcdf/) and [A Brief History of (netCDF) Time](https://www.unidata.ucar.edu/software/netcdf/time/recs.html)\n", + "\n", + "\n", + "\n", + "### Xarray essentials\n", + "Earlier we introduced the xarray pacakge and used `open_dataset` and `open_mfdataset` to retrieve NetCDF data file as an xarray Dataset object. Recall that you can review the data in a notebook by using `print(ds)` or `ds`. \n", + "\n", + "Using xarray to retrieve the aforementioned NetCDF basics is straightforwared. In general,\n", + "* Dataset: `ds`\n", + "* Data array: `ds.variables`\n", + "* Variable: `ds.ds.variables`\n", + "* Dimenssion: `ds.dims`\n", + "* Coordinate: `ds.coords`\n", + "* Data Type: `type`\n", + "* Meta Data (Attributes)\n", + "\n", + "Also, here are some references if you are new to xarray\n", + "* [Xarray in 45 minutes](https://tutorial.xarray.dev/overview/xarray-in-45-min.html)\n", + "* [Handling NetCDF Files using XArray for Absolute Beginners](https://towardsdatascience.com/handling-netcdf-files-using-xarray-for-absolute-beginners-111a8ab4463f)\n", + "\n", + "Try the following commands in action\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b3eac323", + "metadata": {}, + "outputs": [], + "source": [ + "ds = ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77ecf85d", + "metadata": {}, + "outputs": [], + "source": [ + "# Dataset \n", + "ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25e7de09", + "metadata": {}, + "outputs": [], + "source": [ + "# Data array, Variable\n", + "# Note: the info can be overwhelming. Do not attempt to grasp everything at the first glance.\n", + "ds.variables" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c41b67e", + "metadata": {}, + "outputs": [], + "source": [ + "# dimenssions\n", + "ds.dims" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "156f1dfc", + "metadata": {}, + "outputs": [], + "source": [ + "# coordinates\n", + "ds.coords" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "277d6064", + "metadata": {}, + "outputs": [], + "source": [ + "# Meta Data (Attributes)\n", + "ds.attrs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d334681f", + "metadata": {}, + "outputs": [], + "source": [ + "# type\n", + "print(type(ds))\n", + "print(type(ds.variables))\n", + "print(type(ds.dims))\n", + "print(type(ds.coords))\n", + "print(type(ds.attrs))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "643399d6", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: retrieve variable names only\n", + "list(ds.variables)" + ] + }, + { + "cell_type": "markdown", + "id": "1cf36878", + "metadata": {}, + "source": [ + "#### Discussion: variable vs. coordinates vs. dimenssions. \n", + "From [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#):\n", + "\n", + "* Dimession: A dimension may be used to represent a real physical dimension, for example, time, latitude, longitude, or height. A dimension might also be used to index other quantities, for example station or model-run-number.\n", + "A netCDF dimension has both a name and a length. A dimension length is an arbitrary positive integer, except that one dimension in a netCDF dataset can have the length UNLIMITED.\n", + "\n", + "* Variables: Variables are used to store the bulk of the data in a netCDF dataset. A variable represents an array of values of the same type. A scalar value is treated as a 0-dimensional array. A variable has a name, a data type, and a shape described by its list of dimensions specified when the variable is created. A variable may also have associated attributes, which may be added, deleted or changed after the variable is created.\n", + "\n", + "* Coordinate (Variables): It is legal for a variable to have the same name as a dimension. Such variables have no special meaning to the netCDF library. However there is a convention that such variables should be treated in a special way by software using this library.\n", + "A variable with the same name as a dimension is called a coordinate variable. It typically defines a physical coordinate corresponding to that dimension.\n", + "\n", + "\n", + "Tips: By definition, coordinates and dimenssions are also variable. For example ds.time is a coordinate but it is also a special variable. But in practice, when we talk about variable, it implies regular/non-coordinate variable. We will use this convention for the remaining notebook. " + ] + }, + { + "cell_type": "markdown", + "id": "d47120f9", + "metadata": {}, + "source": [ + "### Xarray Variable\n", + "\n", + "\n", + "We can use `ds.variables` to access varialbes of a dataset. But many times viewing print-out the whole datasets can be overwhemling. Instead, we would work on the individual variable (also called Xarray data array.) using the `ds[var_name]` syntax, where `var_name` is the name of the variable. See the following examples in action.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5a57e136", + "metadata": {}, + "outputs": [], + "source": [ + "print(type(ds[\"time\"]))\n", + "ds[\"time\"]" + ] + }, + { + "cell_type": "markdown", + "id": "51ec643e", + "metadata": {}, + "source": [ + "#### Variable properties\n", + "For individual varible, the following properties are mostly used with the assocaited xarray method to retrieve them. (assuming var = ds[var_name])\n", + "* name: `var.name`\n", + "* data content: `var.data`\n", + "* attributes: `var.attrs`\n", + "* dimenstions: `var.dims`\n", + "* data type: `var.data.dtype`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a632a525", + "metadata": {}, + "outputs": [], + "source": [ + "var_name = \"time\"\n", + "var = ds[var_name]\n", + "\n", + "print(\"var.name: \\n\", var.name, \"\\n\")\n", + "print(\"var.data: \\n\", var.data, \"\\n\")\n", + "print(\"var.attrs: \\n\", var.attrs, \"\\n\")\n", + "print(\"var.dims: \\n\", var.dims, \"\\n\")\n", + "print(\"var.data.dtype: \\n\", var.data.dtype, \"\\n\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b256b07f", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: indivdual variable info table (collect and display variable info as a tabular format)\n", + "df_info = pd.DataFrame()\n", + "df_info[\"var_name\"] = list(ds.variables)\n", + "df_info[\"dims\"] = df_info.var_name.apply(lambda x: ds[x].dims)\n", + "df_info[\"is_dim\"] = df_info.var_name.apply(lambda x: x in ds.dims)\n", + "df_info[\"n_dim\"] = df_info.var_name.apply(lambda x: len(ds[x].dims))\n", + "df_info[\"attrs\"] = df_info.var_name.apply(lambda x: ds[x].attrs)\n", + "df_info[\"dtype\"] = df_info.var_name.apply(lambda x: ds[x].data.dtype)\n", + "df_info" + ] + }, + { + "cell_type": "markdown", + "id": "e6a36b96", + "metadata": {}, + "source": [ + "### Data cleaning/Preprocessing (skipped)\n", + "Data cleaning and preprocessing is an important stage in the the data analysis pipeline. However it will be out of the scope of this notebook. For more information about data cleaning and preprocessing basics, here are some references. \n", + "* [Xarray Fundamentals](https://earth-env-data-science.github.io/lectures/xarray/xarray.html)\n", + "* [Xarray Tutorial — Pangeo Gallery documentation](http://gallery.pangeo.io/repos/pangeo-data/pangeo-tutorial-gallery/xarray.html)\n", + "* [Pythonic Data Cleaning With pandas and NumPy](https://realpython.com/python-data-cleaning-numpy-pandas/)\n", + "* [Pandas - Cleaning Data](https://www.w3schools.com/python/pandas/pandas_cleaning.asp)" + ] + }, + { + "cell_type": "markdown", + "id": "ae6e1a69", + "metadata": {}, + "source": [ + "### Plotting\n", + "Data visualization (or plotting) is another important data analysis topic and deserves its own discussion. Here in this notebook we will only demonstrate basic tool for simple data visualization tasks.\n", + "\n", + "Here are some reference you might find useful:\n", + "* xarray plotting: https://docs.xarray.dev/en/stable/user-guide/plotting.html\n", + "* Atmospheric Community Toolkit (ACT): https://arm-doe.github.io/ACT/index.html\n", + "\n", + "Note: this notebook is auto-generated using a template. It uses a general idea to select variable(s) to plot and is not customized to each indivdual notebook. Feel free to change the variables in intrrests, especially certain figure is failed to plot." + ] + }, + { + "cell_type": "markdown", + "id": "7e417fb2", + "metadata": {}, + "source": [ + "#### 1-dimenssional basic time series plot\n", + "\n", + "For the following plot we would like to find variables such that\n", + "* it has one and only one dimession\n", + "* \"time\" is its coordinate variable\n", + "* it is not a dimenssion itself,\n", + "* it is not a special variable with substrings within [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "\n", + "Please see the following example in action. (For more details about pandas filtering, please see the following references.)\n", + "* [pandas: multiple conditions while indexing data frame](https://stackoverflow.com/questions/22591174/pandas-multiple-conditions-while-indexing-data-frame-unexpected-behavior)\n", + "* [pandas.DataFrame.apply](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.apply.html)\n", + "* [How to test if a string contains one of the substrings in a list...](https://stackoverflow.com/questions/26577516/how-to-test-if-a-string-contains-one-of-the-substrings-in-a-list-in-pandas" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30edac2d", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==1, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter = df_info[(df_info.n_dim == 1) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4777a659", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_1d = df_filter.var_name.values[0]\n", + " var_1d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb5ae985", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " ds[var_1d].plot()\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "9e41066e", + "metadata": {}, + "source": [ + "#### 2-dimenssional basic plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09bd4adc", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==2, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_2 = df_info[(df_info.n_dim == 2) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2cdea1a9", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_2d = df_filter_2.var_name.values[0]\n", + " var_2d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5f48879", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " print(ds[var_2d].dims)\n", + " # ds[var_2d].plot()\n", + "\n", + " # conventionally, use \"time\" as x-axis\n", + " ds[var_2d].plot(x=\"time\")\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "7554b2a6", + "metadata": {}, + "source": [ + "#### qc-plotting (optional)\n", + "\n", + "Note: act qc plotting has more strict requirement, one of them is the associated qc variable need to have \"flag_masks\" attributes. Which is added by using ds = act.io.armfiles.read_netcdf(files_list), then ds.clean.cleanup()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71c2096f", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " ds_act = act.io.armfiles.read_netcdf(full_path)\n", + " print(type(ds_act))\n", + " ds_act.clean.cleanup()\n", + "\n", + " # or \n", + " # ds.clean.cleanup()\n", + "except Exception as e:\n", + " print(\"ERROR\", e)\n", + " ds_act = ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "844f8505", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter valid variables for ACT qc plotting\n", + "condition = (\"qc_\" + df_info.var_name).apply(lambda x: ds_act[x].attrs.get(\"flag_masks\") is not None \n", + " if x in list(ds.variables) else False)\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_3 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings))) &\n", + " condition\n", + " ]\n", + "df_filter_3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd38cc6", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "try:\n", + " qc_variable = df_filter_3.var_name.values[0]\n", + " print(qc_variable)\n", + "except Exception as e:\n", + " print(e)\n", + " \n", + "try:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + " qc_ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "d76e4d27", + "metadata": {}, + "source": [ + "#### bonus: choose variables to plot from a dropdown menu " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb733804", + "metadata": {}, + "outputs": [], + "source": [ + "# Valid variables filtering\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_4 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82d5c20d", + "metadata": {}, + "outputs": [], + "source": [ + "# example 1: using xarray plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2fc9aaa4", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# fig, ax = plt.subplots(figsize=(10, 4))\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "# fig.clear() # Remove old lines from plot and plot new one\n", + "# if len(ds[var].dims)==2:\n", + "# ds[var].plot(x=\"time\", add_colorbar=False)\n", + "# else:\n", + "# ds[var].plot()\n", + "# plt.grid()\n", + "# plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fabdd802", + "metadata": {}, + "outputs": [], + "source": [ + "# example 2: using act plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "962b4186", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "\n", + "\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "\n", + "# i_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + "# i_display.add_subplots((1,), figsize=(10, 4))\n", + "# ax = i_display.plot(var, subplot_index=(0,), set_title=f\"{var} ({ds_act[var].attrs['long_name']})\",)\n", + "\n", + "# ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "# ax.grid()\n", + "# plt.show()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/SACRADV3D3C/.ipynb_checkpoints/kasacradv3d3c.c1-checkpoint.ipynb b/VAPs/quicklook/SACRADV3D3C/.ipynb_checkpoints/kasacradv3d3c.c1-checkpoint.ipynb new file mode 100644 index 00000000..8d364ba7 --- /dev/null +++ b/VAPs/quicklook/SACRADV3D3C/.ipynb_checkpoints/kasacradv3d3c.c1-checkpoint.ipynb @@ -0,0 +1,2574 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# KASACRADV3D3C.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/sacradv3d3c) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'kasacradv3d3c'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2012-08-31', 'facility': 'C1', 'site': 'sgp', 'start_date': '2012-08-01'}]" + ] + }, + { + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "ac6764f5", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The following locations and date ranges are available for this VAP:\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
sitefacilitystart_dateend_date
0sgpC12012-08-012012-08-31
\n", + "
" + ], + "text/plain": [ + " site facility start_date end_date\n", + "0 sgp C1 2012-08-01 2012-08-31" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2012-08-29'\n", + "date_end = '2012-08-31'" + ] + }, + { + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'/data/archive/sgp/sgpkasacradv3d3cC1.c1'" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['20120829', '20120830', '20120831']" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "51feea2e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120829.025008.nc',\n", + " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120829.153009.nc',\n", + " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120829.201628.nc',\n", + " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120829.002402.nc',\n", + " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120829.175243.nc',\n", + " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120829.195648.nc',\n", + " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120829.130446.nc',\n", + " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120829.081435.nc',\n", + " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120829.151031.nc',\n", + " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120829.173303.nc',\n", + " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120829.030947.nc',\n", + " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120829.054747.nc',\n", + " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120829.124506.nc',\n", + " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120829.104113.nc',\n", + " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120829.075456.nc',\n", + " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120829.004340.nc',\n", + " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120829.102134.nc',\n", + " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120829.052807.nc',\n", + " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120829.221741.nc',\n", + " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120829.051559.nc',\n", + " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120829.223721.nc',\n", + " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120830.202247.nc',\n", + " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120830.104350.nc',\n", + " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120830.131114.nc',\n", + " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120830.030911.nc',\n", + " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120830.010234.nc',\n", + " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120830.173849.nc',\n", + " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120830.224833.nc',\n", + " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120830.004254.nc',\n", + " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120830.075837.nc',\n", + " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120830.081817.nc',\n", + " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120830.053252.nc',\n", + " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120830.125133.nc',\n", + " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120830.102411.nc',\n", + " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120830.153540.nc',\n", + " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120830.055231.nc',\n", + " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120830.151600.nc',\n", + " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120830.222853.nc',\n", + " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120830.175829.nc',\n", + " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120830.200306.nc',\n", + " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120830.032851.nc',\n", + " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120831.060543.nc',\n", + " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120831.054603.nc',\n", + " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120831.230808.nc',\n", + " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120831.202355.nc',\n", + " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120831.103937.nc',\n", + " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120831.130632.nc',\n", + " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120831.105916.nc',\n", + " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120831.224825.nc',\n", + " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120831.011234.nc',\n", + " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120831.175952.nc',\n", + " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120831.031811.nc',\n", + " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120831.033753.nc',\n", + " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120831.005255.nc',\n", + " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120831.132611.nc',\n", + " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120831.181932.nc',\n", + " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120831.153200.nc',\n", + " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120831.204334.nc',\n", + " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120831.083147.nc',\n", + " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120831.155139.nc',\n", + " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120831.081207.nc']" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "61 files loaded\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
<xarray.Dataset>\n",
+       "Dimensions:                                       (time: 23687, bound: 2,\n",
+       "                                                   height: 201, bin: 28,\n",
+       "                                                   isoline: 4, h_distance: 401,\n",
+       "                                                   frequency: 1)\n",
+       "Coordinates:\n",
+       "  * time                                          (time) datetime64[ns] 2012-...\n",
+       "  * height                                        (height) float32 0.0 ... 10.0\n",
+       "  * bin                                           (bin) float32 -47.5 ... 20.0\n",
+       "  * isoline                                       (isoline) float32 5.0 ... 20.0\n",
+       "  * h_distance                                    (h_distance) float32 -1e+04...\n",
+       "  * frequency                                     (frequency) float32 3.529e+10\n",
+       "Dimensions without coordinates: bound\n",
+       "Data variables: (12/20)\n",
+       "    base_time                                     (time) datetime64[ns] 2012-...\n",
+       "    time_offset                                   (time) datetime64[ns] 2012-...\n",
+       "    time_bounds                                   (time, bound) object dask.array<chunksize=(392, 2), meta=np.ndarray>\n",
+       "    height_bounds                                 (time, height, bound) float32 dask.array<chunksize=(392, 201, 2), meta=np.ndarray>\n",
+       "    bin_bounds                                    (time, bin, bound) float32 dask.array<chunksize=(392, 28, 2), meta=np.ndarray>\n",
+       "    isoline_bounds                                (time, isoline, bound) float32 dask.array<chunksize=(392, 4, 2), meta=np.ndarray>\n",
+       "    ...                                            ...\n",
+       "    cloud_fraction                                (time, isoline, height) float32 dask.array<chunksize=(392, 4, 201), meta=np.ndarray>\n",
+       "    cloud_fraction_std                            (time, isoline, height) float32 dask.array<chunksize=(392, 4, 201), meta=np.ndarray>\n",
+       "    cfad                                          (time, bin, height) float32 dask.array<chunksize=(392, 28, 201), meta=np.ndarray>\n",
+       "    lat                                           (time) float32 36.6 ... 36.6\n",
+       "    lon                                           (time) float32 -97.49 ... -...\n",
+       "    alt                                           (time) float32 318.0 ... 318.0\n",
+       "Attributes: (12/20)\n",
+       "    command_line:          sacradv3d3c -s sgp -f C1 -b 20120829 -n sacradv3d3...\n",
+       "    process_version:       vap-sacradv3d3c-1.1-0.el6\n",
+       "    dod_version:           kasacradv3d3c-c1-1.2\n",
+       "    input_datastreams:     sgpkasacrcorcwrhiC1.c1 : 1.0 : 20120829.002403-201...\n",
+       "    site_id:               sgp\n",
+       "    platform_id:           kasacradv3d3c\n",
+       "    ...                    ...\n",
+       "    radar_beam_width_h:    0.311\n",
+       "    history:               created by user singh on machine amber at 2018-12-...\n",
+       "    _file_dates:           ['20120829', '20120829', '20120829', '20120829', '...\n",
+       "    _file_times:           ['002402', '004340', '025008', '030947', '051559',...\n",
+       "    _datastream:           sgpkasacradv3d3cC1.c1\n",
+       "    _arm_standards_flag:   1
" + ], + "text/plain": [ + "\n", + "Dimensions: (time: 23687, bound: 2,\n", + " height: 201, bin: 28,\n", + " isoline: 4, h_distance: 401,\n", + " frequency: 1)\n", + "Coordinates:\n", + " * time (time) datetime64[ns] 2012-...\n", + " * height (height) float32 0.0 ... 10.0\n", + " * bin (bin) float32 -47.5 ... 20.0\n", + " * isoline (isoline) float32 5.0 ... 20.0\n", + " * h_distance (h_distance) float32 -1e+04...\n", + " * frequency (frequency) float32 3.529e+10\n", + "Dimensions without coordinates: bound\n", + "Data variables: (12/20)\n", + " base_time (time) datetime64[ns] 2012-...\n", + " time_offset (time) datetime64[ns] 2012-...\n", + " time_bounds (time, bound) object dask.array\n", + " height_bounds (time, height, bound) float32 dask.array\n", + " bin_bounds (time, bin, bound) float32 dask.array\n", + " isoline_bounds (time, isoline, bound) float32 dask.array\n", + " ... ...\n", + " cloud_fraction (time, isoline, height) float32 dask.array\n", + " cloud_fraction_std (time, isoline, height) float32 dask.array\n", + " cfad (time, bin, height) float32 dask.array\n", + " lat (time) float32 36.6 ... 36.6\n", + " lon (time) float32 -97.49 ... -...\n", + " alt (time) float32 318.0 ... 318.0\n", + "Attributes: (12/20)\n", + " command_line: sacradv3d3c -s sgp -f C1 -b 20120829 -n sacradv3d3...\n", + " process_version: vap-sacradv3d3c-1.1-0.el6\n", + " dod_version: kasacradv3d3c-c1-1.2\n", + " input_datastreams: sgpkasacrcorcwrhiC1.c1 : 1.0 : 20120829.002403-201...\n", + " site_id: sgp\n", + " platform_id: kasacradv3d3c\n", + " ... ...\n", + " radar_beam_width_h: 0.311\n", + " history: created by user singh on machine amber at 2018-12-...\n", + " _file_dates: ['20120829', '20120829', '20120829', '20120829', '...\n", + " _file_times: ['002402', '004340', '025008', '030947', '051559',...\n", + " _datastream: sgpkasacradv3d3cC1.c1\n", + " _arm_standards_flag: 1" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['wind_speed', 'wind_direction', 'cloud_fraction']" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [ + { + "ename": "TypeError", + "evalue": "Dimensions of C (201, 4, 23687) should be one smaller than X(23687) and Y(4) while using shading='flat' see help(pcolormesh)", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[0;32mIn[9], line 5\u001b[0m\n\u001b[1;32m 2\u001b[0m ts_display\u001b[38;5;241m.\u001b[39madd_subplots((\u001b[38;5;28mlen\u001b[39m(variables_to_plot),), figsize \u001b[38;5;241m=\u001b[39m (\u001b[38;5;241m9.5\u001b[39m,\u001b[38;5;241m4\u001b[39m\u001b[38;5;241m*\u001b[39m\u001b[38;5;28mlen\u001b[39m(variables_to_plot)))\n\u001b[1;32m 4\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m i,v \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28menumerate\u001b[39m(variables_to_plot):\n\u001b[0;32m----> 5\u001b[0m ts_ax \u001b[38;5;241m=\u001b[39m \u001b[43mts_display\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mplot\u001b[49m\u001b[43m(\u001b[49m\u001b[43mv\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43msubplot_index\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mi\u001b[49m\u001b[43m,\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mset_title\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mds\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mvariables\u001b[49m\u001b[43m[\u001b[49m\u001b[43mv\u001b[49m\u001b[43m]\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mattrs\u001b[49m\u001b[43m[\u001b[49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43mlong_name\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m,\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 6\u001b[0m ts_ax\u001b[38;5;241m.\u001b[39mgrid()\n\u001b[1;32m 8\u001b[0m plt\u001b[38;5;241m.\u001b[39mshow()\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/act/plotting/timeseriesdisplay.py:588\u001b[0m, in \u001b[0;36mTimeSeriesDisplay.plot\u001b[0;34m(self, field, dsname, subplot_index, cmap, set_title, add_nan, day_night_background, invert_y_axis, abs_limits, time_rng, y_rng, use_var_for_y, set_shading, assessment_overplot, overplot_marker, overplot_behind, overplot_markersize, assessment_overplot_category, assessment_overplot_category_color, force_line_plot, labels, cbar_label, cbar_h_adjust, secondary_y, y_axis_flag_meanings, colorbar_labels, cb_friendly, **kwargs)\u001b[0m\n\u001b[1;32m 586\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124medgecolors\u001b[39m\u001b[38;5;124m'\u001b[39m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;129;01min\u001b[39;00m kwargs\u001b[38;5;241m.\u001b[39mkeys():\n\u001b[1;32m 587\u001b[0m kwargs[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124medgecolors\u001b[39m\u001b[38;5;124m'\u001b[39m] \u001b[38;5;241m=\u001b[39m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mface\u001b[39m\u001b[38;5;124m'\u001b[39m\n\u001b[0;32m--> 588\u001b[0m mesh \u001b[38;5;241m=\u001b[39m \u001b[43max\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mpcolormesh\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 589\u001b[0m \u001b[43m \u001b[49m\u001b[43mnp\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43masarray\u001b[49m\u001b[43m(\u001b[49m\u001b[43mxdata\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 590\u001b[0m \u001b[43m \u001b[49m\u001b[43mydata\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 591\u001b[0m \u001b[43m \u001b[49m\u001b[43mdata\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mtranspose\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 592\u001b[0m \u001b[43m \u001b[49m\u001b[43mshading\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mset_shading\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 593\u001b[0m \u001b[43m \u001b[49m\u001b[43mcmap\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcmap\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 594\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 595\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 597\u001b[0m \u001b[38;5;66;03m# Set Title\u001b[39;00m\n\u001b[1;32m 598\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m set_title \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/matplotlib/__init__.py:1442\u001b[0m, in \u001b[0;36m_preprocess_data..inner\u001b[0;34m(ax, data, *args, **kwargs)\u001b[0m\n\u001b[1;32m 1439\u001b[0m \u001b[38;5;129m@functools\u001b[39m\u001b[38;5;241m.\u001b[39mwraps(func)\n\u001b[1;32m 1440\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21minner\u001b[39m(ax, \u001b[38;5;241m*\u001b[39margs, data\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mNone\u001b[39;00m, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs):\n\u001b[1;32m 1441\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m data \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[0;32m-> 1442\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mfunc\u001b[49m\u001b[43m(\u001b[49m\u001b[43max\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;28;43mmap\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43msanitize_sequence\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43margs\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 1444\u001b[0m bound \u001b[38;5;241m=\u001b[39m new_sig\u001b[38;5;241m.\u001b[39mbind(ax, \u001b[38;5;241m*\u001b[39margs, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs)\n\u001b[1;32m 1445\u001b[0m auto_label \u001b[38;5;241m=\u001b[39m (bound\u001b[38;5;241m.\u001b[39marguments\u001b[38;5;241m.\u001b[39mget(label_namer)\n\u001b[1;32m 1446\u001b[0m \u001b[38;5;129;01mor\u001b[39;00m bound\u001b[38;5;241m.\u001b[39mkwargs\u001b[38;5;241m.\u001b[39mget(label_namer))\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/matplotlib/axes/_axes.py:6220\u001b[0m, in \u001b[0;36mAxes.pcolormesh\u001b[0;34m(self, alpha, norm, cmap, vmin, vmax, shading, antialiased, *args, **kwargs)\u001b[0m\n\u001b[1;32m 6217\u001b[0m shading \u001b[38;5;241m=\u001b[39m shading\u001b[38;5;241m.\u001b[39mlower()\n\u001b[1;32m 6218\u001b[0m kwargs\u001b[38;5;241m.\u001b[39msetdefault(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124medgecolors\u001b[39m\u001b[38;5;124m'\u001b[39m, \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mnone\u001b[39m\u001b[38;5;124m'\u001b[39m)\n\u001b[0;32m-> 6220\u001b[0m X, Y, C, shading \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_pcolorargs\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43mpcolormesh\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 6221\u001b[0m \u001b[43m \u001b[49m\u001b[43mshading\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mshading\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mkwargs\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 6222\u001b[0m coords \u001b[38;5;241m=\u001b[39m np\u001b[38;5;241m.\u001b[39mstack([X, Y], axis\u001b[38;5;241m=\u001b[39m\u001b[38;5;241m-\u001b[39m\u001b[38;5;241m1\u001b[39m)\n\u001b[1;32m 6223\u001b[0m \u001b[38;5;66;03m# convert to one dimensional array, except for 3D RGB(A) arrays\u001b[39;00m\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/matplotlib/axes/_axes.py:5751\u001b[0m, in \u001b[0;36mAxes._pcolorargs\u001b[0;34m(self, funcname, shading, *args, **kwargs)\u001b[0m\n\u001b[1;32m 5749\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m shading \u001b[38;5;241m==\u001b[39m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mflat\u001b[39m\u001b[38;5;124m'\u001b[39m:\n\u001b[1;32m 5750\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m (Nx, Ny) \u001b[38;5;241m!=\u001b[39m (ncols \u001b[38;5;241m+\u001b[39m \u001b[38;5;241m1\u001b[39m, nrows \u001b[38;5;241m+\u001b[39m \u001b[38;5;241m1\u001b[39m):\n\u001b[0;32m-> 5751\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mTypeError\u001b[39;00m(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mDimensions of C \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mC\u001b[38;5;241m.\u001b[39mshape\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m should\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 5752\u001b[0m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m be one smaller than X(\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mNx\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m) and Y(\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mNy\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m)\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 5753\u001b[0m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m while using shading=\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mflat\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 5754\u001b[0m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m see help(\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mfuncname\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m)\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[1;32m 5755\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m: \u001b[38;5;66;03m# ['nearest', 'gouraud']:\u001b[39;00m\n\u001b[1;32m 5756\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m (Nx, Ny) \u001b[38;5;241m!=\u001b[39m (ncols, nrows):\n", + "\u001b[0;31mTypeError\u001b[0m: Dimensions of C (201, 4, 23687) should be one smaller than X(23687) and Y(4) while using shading='flat' see help(pcolormesh)" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "8189dde8d6e0443cb4732d2453c2a30b", + "version_major": 2, + "version_minor": 0 + }, + "image/png": "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nEQigW3btsl6C763bds2mKbZ5d9+06ZN6eOT6ecpPObexHO88PEcp+7wHCdSK+B0BYhod/F4HOeffz4sy8JDDz3U6WczZszY7fmapnX6t0gmnNj1cTvPITm6+tvv6djs+hiPuffwHPcWnuP0dTzHidTjiC2Ry8TjcZx33nlYuXIlXnvttU69vF2prq7u1PMPAFu2bAGws8c303MCgQB69eqVx9rTrnr37g3DMLr821dVVaG6uhoAMv48Ex7zwsZz3Dt4jlNXeI4TOYOBLZGLpG6GX3zxBV5//XVbN6tjjz0W8+bNQywWSz/26quvoqamBgMHDkw/57XXXuv0e6+++ipGjRqFYDCY1/dAO4VCIYwcOXK3v/1rr72G4447DoMGDUJ1dXWnn8diMcydOxfHHXdcxtflMS9cPMe9hec4fR3PcSIHOZGxisivmpqaxEcffSQ++ugjAUDcd9994qOPPhKrV68W8XhcnH766WLAgAFi8eLFYuPGjemvaDSafo2pU6eKb3/72+l/19fXi6qqKnHBBReIJUuWiH/+85+ivLy8y20CbrjhBrFs2TLxv//7v9wmQJHUViD/+7//K5YtWyauv/56UVJSIlatWiWE6NgKpKKiQvzzn/8US5YsERdccMFuW4HwmBcOnuP+w3PcX3iOE7kXA1sihWbPni0A7PZ16aWXipUrV3b5MwBi9uzZ6de49NJLxdixYzu97ieffCLGjBkjwuGwqK6uFtOnT09vEZAyZ84cccQRR4hQKCQGDhwoHn74YQXvmIQQ4g9/+IPYd999RSgUEiNGjBBz585N/8yyLHHbbbeJ6upqEQ6HxQknnCCWLFnS6fd5zAsHz3F/4jnuHzzHidxLEyK58pyIiIiIiIioAHGNLRERERERERU0BrZERERERERU0BjYEhERERERUUFjYEtEREREREQFjYEtERERERERFTQGtkRERERERFTQGNgSERERERFRQWNgS0RERERERAWNgS0REREREREVNAa2REREREREVNAY2BIREREREVFBY2BLREREREREBY2BLRERERERERU0BrZEROR6zzzzDDRNw5NPPrnbzw477DBomoZXXnllt58NHjwYI0aMAADMmTMHmqZhzpw5eavXqlWroGkaHn/88Zx+//HHH4emaVi1alX6sXHjxmHcuHF5qV82NmzYgOnTp2Px4sW7/Wz69OnQNE15nYiIiOxiYEtERK43btw4aJqG2bNnd3q8rq4OS5YsQUlJyW4/W7duHb766iuMHz8eADBixAi888476UDXrR566CE89NBDysvdsGEDbr/99i4D2yuvvBLvvPOO8joRERHZFXC6AkRERHvSu3dvHHLIIbuNts6dOxeBQADf+c53dgtsU/9OBbbl5eU45phjlNR3bxx00EF7fI5pmkgkEgiHwwpqBAwYMAADBgxQUhYREVEuOGJLREQFYfz48Vi+fDk2btyYfmzOnDk48sgjMXnyZCxatAhNTU2dfmYYBsaMGZP+99enIl922WUoLS3Fl19+icmTJ6O0tBS1tbX48Y9/jGg02qn8DRs24LzzzkNZWRkqKiowZcoUbNq0yXb93333XYwePRqRSAQ1NTWYNm0a4vH4bs/7+lTk1HTn3/zmN/jlL3+JQYMGIRwOpwP3Dz74AKeffjoqKysRiURwxBFH4KmnntrtddevX4+rr74atbW1CIVCqKmpwTnnnIPNmzen/44AcPnll0PTNGiahunTpwPoeiqyZVn4zW9+g2HDhiEcDqNv37645JJLsG7dut3ezyGHHIKFCxdizJgxKC4uxn777Ye77roLlmXZ/vsRERF1h4EtEREVhNTI666B6ezZszF27FiMHj0amqZh/vz5nX42YsQIVFRUdPu68Xgcp59+OiZMmIAXXngBV1xxBe6//37cfffd6ee0tbVh4sSJePXVVzFjxgw8/fTTqK6uxpQpU2zVfdmyZZgwYQLq6+vx+OOP45FHHsFHH32EX/7yl7bf/+9+9zu8+eabuPfee/Gf//wHw4YNw+zZszF69GjU19fjkUcewQsvvIDDDz8cU6ZM6bTud/369TjyyCPx3HPP4cYbb8R//vMfPPDAA6ioqMCOHTswYsQIzJw5EwBwyy234J133sE777yDK6+8MmN9vve97+Gmm27CSSedhBdffBF33nknXn75ZRx33HHYtm1bp+du2rQJF110ES6++GK8+OKL+MY3voFp06bhr3/9q+33T0RE1C1BRERUAOrq6oSu6+Lqq68WQgixbds2oWmaePnll4UQQhx11FHiJz/5iRBCiDVr1ggA4mc/+1n692fPni0AiNmzZ6cfu/TSSwUA8dRTT3Uqa/LkyWLo0KHpfz/88MMCgHjhhRc6Pe+qq64SAMTMmTO7rfuU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aSb309ChQ9WMGTNUQ0ND+2PTpk1Gt7dUJvfTn/70J9WnTx916623qrfeeku9+eab6o9//KPR7S2FqX2UTqc7fIYaGhrUueeeq3bbbTflOI7x7S6Wyc/SnnvuqU444QT173//W7399tvq/PPPV1VVVaqhocHoNpfC1H5atWqV6tOnj5o0aZJ688031YsvvqjGjBmjTj75ZOPbXKxy7aNf/OIXaubMmWrmzJlKRNQnn3yyXVlBvv/2KwLbgFi7dq0SEfXMM88opZRyHEfV1dWp6667rv01ra2tqqamRt12221KKaU2btyoEomEeuihh9pfs2rVKmXbtvrnP/+plFJq2bJlSkTU888/3/6ahQsXKhFRb775polNKytd+2lbs2bNClRg+1km9lHBz3/+czVs2DBNW6KXyf30l7/8RVmWpTKZjKat0UP3PpozZ47ad9991euvvx6owPazdO6noUOHql/84hdmNkQzXfspm82qIUOGqLvuusvg1uhh6rqUyWTUwIED1YwZMzRujT669tPHH3+sRETNmzev/TVNTU1KRNSTTz5pYtPKStd+uv3229XAgQNVPp9vf83ixYuViKh33nnHxKaVTSn7aFtPPfVUp4Ft2O6//YJU5IBobGwUEZG+ffuKiMjy5ctlzZo1Mn78+PbXpFIpGTdunCxYsEBERBYtWiTZbLbDawYPHiwjR45sf83ChQulpqZGDjnkkPbXHHrooVJTU9P+miDRtZ/CxOQ+amxsbC8naEztpw0bNsgDDzwgY8aMkUQioWtztNC5jz766CP57ne/K7/97W+lqqrKxOZoo/uzdP3110u/fv3kc5/7nPzsZz8LTPr/Z+naT6+88oqsWrVKbNuWz3/+8zJo0CA5/vjjt0sdDAJT16XHHntM1q1bJ2effbamLdFL137q16+f7LfffnL//ffLli1bJJfLye233y61tbUyatQoU5tXNrr2UzqdlmQyKbb9aZhRWVkpIiLz58/Xu1FlVso+ciNs999+QWAbAEopmTJlinzxi1+UkSNHiojImjVrRESktra2w2tra2vb/2/NmjWSTCalT58+3b5m4MCB25U5cODA9tcEhc79FBYm99G7774rN998s0yaNKncm6Gdif102WWXSY8ePaRfv36yYsUK+ctf/qJrc7TQuY+UUnL22WfLpEmTZPTo0bo3RSvdn6WLLrpIHnroIXnqqafkBz/4gdx0001y/vnn69wkLXTup/fee09ERKZPny4//vGP5W9/+5v06dNHxo0bJxs2bNC6XeVk8vp99913y7HHHiv19fXl3gztdO4ny7Jk7ty5snjxYunVq5dUVFTIL37xC/nnP/8ZqLk3RPTup6OOOkrWrFkjN9xwg2QyGfnkk0/kiiuuEBGRhoYGrdtVTqXuIzfCdP/tJwS2AfCDH/xAXn31VXnwwQe3+z/Lsjr8rpTa7rnP+uxrOnu9m/fxG937KQxM7aPVq1fLcccdJ9/61rfk3HPP3blKe8DEfrr00ktl8eLF8sQTT0gsFpMJEyaIUmrnK2+Izn108803S1NTk0ydOrV8FfaI7s/SxRdfLOPGjZMDDjhAzj33XLntttvk7rvvlvXr15dnAwzRuZ8cxxERkSuvvFJOPvlkGTVqlMyaNUssy5I//vGPZdoC/Uxdvz/88EN5/PHH5Zxzztm5CntE535SSsn5558vAwcOlGeffVZefPFF+frXvy4nnnhioAI2Eb37acSIEXLffffJ//7v/0pVVZXU1dXJ7rvvLrW1tRKLxcq3EZqVex/t6D1KfR98isDW5374wx/KY489Jk899ZTssssu7c/X1dWJiGzXqrN27dr2VqS6urr2lrLuXvPRRx9tV+7HH3+8XWuUn+neT2Fgah+tXr1ajjzySDnssMPkjjvu0LEpWpnaT/3795e9995bjjnmGHnooYdkzpw58vzzz+vYpLLTvY/+9a9/yfPPPy+pVEri8bjsueeeIiIyevRoOeuss7RtV7l5cV069NBDRUTkP//5T1m2wQTd+2nQoEEiIjJ8+PD2/0+lUrL77rvLihUryr9BGpj8LM2aNUv69esnX/va18q9GdqZuDb97W9/k4ceekjGjh0rBx10kNxyyy1SWVkp9913n85NKysTn6fTTz9d1qxZI6tWrZL169fL9OnT5eOPP5Zhw4bp2qyy2pl95EZY7r99R+sIXpTMcRx1wQUXqMGDB6u333670/+vq6tT119/fftz6XS60wH+s2fPbn/N6tWrO5086oUXXmh/zfPPPx+Yweum9tO2gjZ5lMl99OGHH6q99tpLnXrqqZ3OcOtnXnyWClasWKFERD311FPl2yANTO2jDz74QC1durT98fjjjysRUX/605/UypUrNW/lzvPys/TXv/5ViYj64IMPyrhFepjaT42NjSqVSnWYPKowOdLtt9+ua/PKwvRnyXEcNWzYsMDMZl9gaj899thjyrbt7WYe33vvvdXPfvYzHZtWVl5em+6++25VVVXV6ezAflKOfbStHU0eFdT7b78isPWp73//+6qmpkY9/fTTHabfb25ubn/Nddddp2pqatQjjzyili5dqk477bROp2TfZZdd1JNPPqleeeUVddRRR3W63M8BBxygFi5cqBYuXKj233//wEw3bnI/ffDBB2rx4sXq6quvVj179lSLFy9Wixcv9v3SGqb20apVq9See+6pjjrqKPXhhx92KCsITO2nF154Qd18881q8eLF6v3331f/+te/1Be/+EW1xx57qNbWVuPbXQyT59u2li9fHqhZkU3tpwULFqgbb7xRLV68WL333ntq9uzZavDgweprX/ua8W0uhcnP00UXXaSGDBmiHn/8cfXmm2+qc845Rw0cOFBt2LDB6DYXy/Q59+STTyoRUcuWLTO2jeVgaj99/PHHql+/fuqb3/ymWrJkiXrrrbfUJZdcohKJhFqyZInx7S6Wyc/TzTffrBYtWqTeeust9etf/1pVVlaqX/7yl0a3txTl2kcNDQ1q8eLF6s4772yfSXvx4sVq/fr17a8J8v23XxHY+pSIdPqYNWtW+2scx1HTpk1TdXV1KpVKqS996Utq6dKlHd6npaVF/eAHP1B9+/ZVlZWV6sQTT1QrVqzo8Jr169erM844Q/Xq1Uv16tVLnXHGGb5vUSswuZ/OOuusTsvyey+bqX00a9asLssKAlP76dVXX1VHHnmk6tu3r0qlUmq33XZTkyZNUh9++KGpTS2ZyfNtW0ELbE3tp0WLFqlDDjlE1dTUqIqKCrXPPvuoadOmqS1btpja1J1i8vOUyWTUj370IzVw4EDVq1cvdfTRR6vXXnvNxGbuFNPn3GmnnabGjBmje7PKzuR+eumll9T48eNV3759Va9evdShhx6q5syZY2Izd5rJ/XTmmWeqvn37qmQyqQ444AB1//33m9jEnVaufTRt2rQdvk+Q77/9ylIqQLOVAAAAAADwGUweBQAAAAAINAJbAAAAAECgEdgCAAAAAAKNwBYAAAAAEGgEtgAAAACAQCOwBQAAAAAEGoEtAAAAACDQCGwBAAAAAIFGYAsAAAAACDQCWwAAAABAoBHYAgAAAAACjcAWAAAAABBoBLYAAAAAgEAjsAUAAAAABBqBLQAAAAAg0AhsAQAAAACBRmALAAAAAAg0AlsAAAAAQKAR2AIAAAAAAo3AFgAAAAAQaAS2AAAAAIBAI7AFAAAAAAQagS0AAAAAINAIbAEAAAAAgUZgCwAAAAAINAJbAAAAAECgEdgCAAAAAAKNwBYAAAAAEGgEtgAAAACAQCOwBQAAAAAEGoEtAAAAACDQCGwBAAAAAIFGYAsAAAAACDQCWwAAAABAoBHYAgAAAAACjcAWAAAAABBoBLYAAAAAgEAjsAUAAAAABBqBLQAAAAAg0AhsAQAAAACBRmALAAAAAAg0AlsAAAAAQKAR2AIAAAAAAo3AFgAAAAAQaAS2AAAAAIBAI7AFAAAAAAQagS0AAAAAINAIbAEAAAAAgUZgCwAAAAAINAJbAAAAAECgEdgCAAAAAAKNwBYAAAAAEGgEtgAAAACAQCOwBQAAAAAEGoEtAAAAACDQCGwBAAAAAIFGYAsAAAAACDQCWwAAAABAoBHYAgAAAAACjcAWAAAAABBoBLYAAAAAgEAjsAUAAAAABBqBLQAAAAAg0AhsAQAAAACBRmALAAAAAAg0AlsAAAAAQKAR2AIAAOD/t18HJAAAAACC/r9uR6AvBFgTWwAAANbEFgAAgDWxBQAAYE1sAQAAWBNbAAAA1sQWAACANbEFAABgTWwBAABYE1sAAADWxBYAAIA1sQUAAGBNbAEAAFgTWwAAANbEFgAAgDWxBQAAYE1sAQAAWBNbAAAA1sQWAACANbEFAABgTWwBAABYE1sAAADWxBYAAIA1sQUAAGBNbAEAAFgTWwAAANbEFgAAgDWxBQAAYE1sAQAAWBNbAAAA1sQWAACANbEFAABgTWwBAABYE1sAAADWxBYAAIA1sQUAAGBNbAEAAFgTWwAAANbEFgAAgDWxBQAAYE1sAQAAWBNbAAAA1sQWAACANbEFAABgLRIMdYlK/mrBAAAAAElFTkSuQmCC", + "text/html": [ + "\n", + "
\n", + "
\n", + " Figure\n", + "
\n", + " \n", + "
\n", + " " + ], + "text/plain": [ + "Canvas(toolbar=Toolbar(toolitems=[('Home', 'Reset original view', 'home', 'home'), ('Back', 'Back to previous …" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'wind_speed'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/SACRADV3D3C/SACRADV3D3C_tutorial.ipynb b/VAPs/quicklook/SACRADV3D3C/SACRADV3D3C_tutorial.ipynb new file mode 100644 index 00000000..8186ebb2 --- /dev/null +++ b/VAPs/quicklook/SACRADV3D3C/SACRADV3D3C_tutorial.ipynb @@ -0,0 +1,838 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# KASACRADV3D3C.C1 Notebook\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/sacradv3d3c) for more information about this vap." + ] + }, + { + "cell_type": "markdown", + "id": "97097763", + "metadata": {}, + "source": [ + "In this notebook, we demonstrate the workflow to explore ARM vap data (using kasacradv3d3c as an example.) Value-added products (VAPs) are higher-order data products that have been analyzed and processed to ease scientists’ use of ARM data in atmospheric research and global climate models. \n", + "Here is the main content we will cover." + ] + }, + { + "cell_type": "markdown", + "id": "eddec40f", + "metadata": {}, + "source": [ + "# Table of Content\n", + "## Access the data\n", + "* How to retrieve the data\n", + "* Data path and file name conventions\n", + "* Load data\n", + "## Explore the data\n", + "* NetCDF Data structure\n", + "* Xarray essentials\n", + "* Xarray Variable\n", + "## Plot the data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "import random\n", + "\n", + "import glob\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "2372d5be", + "metadata": {}, + "source": [ + "## Access the data" + ] + }, + { + "cell_type": "markdown", + "id": "5b0d9684", + "metadata": {}, + "source": [ + "### How to retrieve the data\n", + "We assume the path \"/data/archive\" is available where you are running this notebook. You can use `os.path.exists(\"/data/archive\")` to verify if the path exists at your machine.\n", + "\n", + "\n", + "### Data path and file name conventions\n", + "There several common terminologies regarding ARM data, for example, data-stream-name, data-level, site, facility, instrument, etc. (For more details, please see [ARM Data File Standards Version 1.2](https://www.google.com/search?q=arm+datastream+facility+cite+definition&rlz=1C1GCEJ_enUS1029US1029&ei=hb41ZICrBPukqtsPvMODIA&ved=0ahUKEwjAgeiS0aL-AhV7kmoFHbzhAAQQ4dUDCBA&uact=5&oq=arm+datastream+facility+cite+definition&gs_lcp=Cgxnd3Mtd2l6LXNlcnAQAzIFCAAQogQyBQgAEKIEMgUIABCiBDIFCAAQogQyBQgAEKIEOgoIABBHENYEELADOgoIIRCgARDDBBAKSgQIQRgAUJ4DWIoOYO0PaAFwAXgAgAGMAYgBuQmSAQMyLjmYAQCgAQHIAQjAAQE&sclient=gws-wiz-serp).) \n", + "\n", + "For example, this notebook is called `kasacradv3d3c.c1`, where `kasacradv3d3c` is the \"datastream name\", and the `{process.ds_class_level}}` is the \"data level\".\n", + "\n", + "This datastream also contains site `sgp` and facility `C1`. (Note: individual datastream might have multiple site-facility pairs.)\n", + "In such a case, the data of this data-stream is stored at `/data/archive/sgp/sgpkasacradv3d3cC1.c1`, which is in the format of `//.`. We can use the following method to assign the data-stream directory `datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )`\n", + "\n", + "The data files under datastream_dir also follows naming conventions. But once reach the datastream_dir level, the most import file naming convention to differentiate the files is \"yyyyMMdd.hhmmss\", which comes handy to filter out files based on datetime. For example, we can use `glob.glob(f'{datastream_dir}/*.200709*.*')` to filter files in 2007 September.\n", + "\n", + "(Note: refer to the https://adc.arm.gov/discovery/#/ and https://adc.arm.gov/solr8/metadata/select API to explore ARM datastream and assoicated available sites and facilities\n", + "\n", + "Please see the following examples in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d7e9eb85", + "metadata": {}, + "outputs": [], + "source": [ + "# Verify if DATA_DIR path exists\n", + "DATA_DIR = \"/data/archive\"\n", + "os.path.exists(DATA_DIR)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "586993fd", + "metadata": {}, + "outputs": [], + "source": [ + "# Speicify datastream_dir following the path conventions and check its existence\n", + "DATASTREAM_NAME = \"kasacradv3d3c\"\n", + "DATA_LEVEL = \"c1\"\n", + "site = \"sgp\"\n", + "facility = \"C1\"\n", + "datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )\n", + "print(datastream_dir)\n", + "print(os.path.exists(datastream_dir))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0742f7c1", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: list 5 (random) files under datastream_dir\n", + "files = os.listdir(datastream_dir)\n", + "files[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39b98a36", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: get most recent file\n", + "list_of_files = glob.glob(f\"{datastream_dir}/*\") # * means all if need specific format then *.csv\n", + "latest_file = max(list_of_files, key=os.path.getctime)\n", + "latest_file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "902d514e", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus sort datastream files based on datetime\n", + "files = os.listdir(datastream_dir)\n", + "file_sorted = files.copy()\n", + "file_sorted.sort() \n", + "print(file_sorted[:5])\n", + "\n", + "# to reverse\n", + "file_sorted_reverse = files.copy() \n", + "file_sorted_reverse.sort(reverse=True)\n", + "print(file_sorted_reverse[:5])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec5923b2", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: pattern matching\n", + "# filter the 200709** files under datastream_dir\n", + "files_filter = glob.glob(f'{datastream_dir}/*.200709*.*')\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c4a4aa18", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: use armnotebook_utils.file_filter (TODO) to filter files based on datastream info \n" + ] + }, + { + "cell_type": "markdown", + "id": "cfeb9efc", + "metadata": {}, + "source": [ + "### Load data\n", + "The arm data is stored in [NetCDF](https://en.wikipedia.org/wiki/NetCDF#:~:text=%22NetCDF%20(network%20Common%20Data%20Form,format%20for%20representing%20scientific%20data.) format. We can use xarray's `open_dataset` method to load single file, or `open_mfdataset` to open multiple files. (Note: the latter will still return a single xarray dataset object by combining multiple files.)\n", + "\n", + "Note: open_dataset keeps the file handle open and lazy loads its contents. All parameters are passed directly to open_dataset. It is a preferable method over load_dataset for memory-effiency. (more details, please see [xarray.open_dataset](https://docs.xarray.dev/en/stable/generated/xarray.open_dataset.html))\n", + "\n", + "See the following example in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a440a329", + "metadata": {}, + "outputs": [], + "source": [ + "# open single file\n", + "full_path = latest_file\n", + "print(full_path)\n", + "ds_single = xr.open_dataset(full_path)\n", + "ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0143a3d", + "metadata": {}, + "outputs": [], + "source": [ + "print(ds_single)\n", + "print(type(ds_single))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c0f8939", + "metadata": {}, + "outputs": [], + "source": [ + "# open multiple files\n", + "n_files = 3\n", + "full_paths = [os.path.join(datastream_dir, f_path) for f_path in file_sorted_reverse[:n_files]]\n", + "print(full_paths)\n", + "try: # Note: sometimes multiple files cannot be merged, so we used try except here.\n", + " ds_mutiple = xr.open_mfdataset(full_paths)\n", + " # ds_mutiple\n", + " print(type(ds_mutiple))\n", + " print(ds_mutiple)\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "3fa59943", + "metadata": {}, + "source": [ + "## Explore the data" + ] + }, + { + "cell_type": "markdown", + "id": "571f69b9", + "metadata": {}, + "source": [ + "### NetCDF Data structure \n", + "\n", + "\n", + "(If you are confident with NetCDF basics and xarray essentials, feel free to skip this session.)\n", + "Before we dive into data exploration, there are some eseential concpets we should be familiar with (shown below): \n", + "* Dataset\n", + "* Data array\n", + "* Variable\n", + "* Dimenssion\n", + "* Coordinate\n", + "* Data Type\n", + "* Meta Data (Attributes)\n", + "\n", + "We will not go into details about NetCDF basics and here are some references you might find helpful\n", + "* [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#)\n", + "* [Network Common Data Form (NetCDF)](https://www.unidata.ucar.edu/software/netcdf/) and [A Brief History of (netCDF) Time](https://www.unidata.ucar.edu/software/netcdf/time/recs.html)\n", + "\n", + "\n", + "\n", + "### Xarray essentials\n", + "Earlier we introduced the xarray pacakge and used `open_dataset` and `open_mfdataset` to retrieve NetCDF data file as an xarray Dataset object. Recall that you can review the data in a notebook by using `print(ds)` or `ds`. \n", + "\n", + "Using xarray to retrieve the aforementioned NetCDF basics is straightforwared. In general,\n", + "* Dataset: `ds`\n", + "* Data array: `ds.variables`\n", + "* Variable: `ds.ds.variables`\n", + "* Dimenssion: `ds.dims`\n", + "* Coordinate: `ds.coords`\n", + "* Data Type: `type`\n", + "* Meta Data (Attributes)\n", + "\n", + "Also, here are some references if you are new to xarray\n", + "* [Xarray in 45 minutes](https://tutorial.xarray.dev/overview/xarray-in-45-min.html)\n", + "* [Handling NetCDF Files using XArray for Absolute Beginners](https://towardsdatascience.com/handling-netcdf-files-using-xarray-for-absolute-beginners-111a8ab4463f)\n", + "\n", + "Try the following commands in action\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b3eac323", + "metadata": {}, + "outputs": [], + "source": [ + "ds = ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77ecf85d", + "metadata": {}, + "outputs": [], + "source": [ + "# Dataset \n", + "ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25e7de09", + "metadata": {}, + "outputs": [], + "source": [ + "# Data array, Variable\n", + "# Note: the info can be overwhelming. Do not attempt to grasp everything at the first glance.\n", + "ds.variables" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c41b67e", + "metadata": {}, + "outputs": [], + "source": [ + "# dimenssions\n", + "ds.dims" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "156f1dfc", + "metadata": {}, + "outputs": [], + "source": [ + "# coordinates\n", + "ds.coords" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "277d6064", + "metadata": {}, + "outputs": [], + "source": [ + "# Meta Data (Attributes)\n", + "ds.attrs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d334681f", + "metadata": {}, + "outputs": [], + "source": [ + "# type\n", + "print(type(ds))\n", + "print(type(ds.variables))\n", + "print(type(ds.dims))\n", + "print(type(ds.coords))\n", + "print(type(ds.attrs))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "643399d6", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: retrieve variable names only\n", + "list(ds.variables)" + ] + }, + { + "cell_type": "markdown", + "id": "1cf36878", + "metadata": {}, + "source": [ + "#### Discussion: variable vs. coordinates vs. dimenssions. \n", + "From [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#):\n", + "\n", + "* Dimession: A dimension may be used to represent a real physical dimension, for example, time, latitude, longitude, or height. A dimension might also be used to index other quantities, for example station or model-run-number.\n", + "A netCDF dimension has both a name and a length. A dimension length is an arbitrary positive integer, except that one dimension in a netCDF dataset can have the length UNLIMITED.\n", + "\n", + "* Variables: Variables are used to store the bulk of the data in a netCDF dataset. A variable represents an array of values of the same type. A scalar value is treated as a 0-dimensional array. A variable has a name, a data type, and a shape described by its list of dimensions specified when the variable is created. A variable may also have associated attributes, which may be added, deleted or changed after the variable is created.\n", + "\n", + "* Coordinate (Variables): It is legal for a variable to have the same name as a dimension. Such variables have no special meaning to the netCDF library. However there is a convention that such variables should be treated in a special way by software using this library.\n", + "A variable with the same name as a dimension is called a coordinate variable. It typically defines a physical coordinate corresponding to that dimension.\n", + "\n", + "\n", + "Tips: By definition, coordinates and dimenssions are also variable. For example ds.time is a coordinate but it is also a special variable. But in practice, when we talk about variable, it implies regular/non-coordinate variable. We will use this convention for the remaining notebook. " + ] + }, + { + "cell_type": "markdown", + "id": "d47120f9", + "metadata": {}, + "source": [ + "### Xarray Variable\n", + "\n", + "\n", + "We can use `ds.variables` to access varialbes of a dataset. But many times viewing print-out the whole datasets can be overwhemling. Instead, we would work on the individual variable (also called Xarray data array.) using the `ds[var_name]` syntax, where `var_name` is the name of the variable. See the following examples in action.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5a57e136", + "metadata": {}, + "outputs": [], + "source": [ + "print(type(ds[\"time\"]))\n", + "ds[\"time\"]" + ] + }, + { + "cell_type": "markdown", + "id": "51ec643e", + "metadata": {}, + "source": [ + "#### Variable properties\n", + "For individual varible, the following properties are mostly used with the assocaited xarray method to retrieve them. (assuming var = ds[var_name])\n", + "* name: `var.name`\n", + "* data content: `var.data`\n", + "* attributes: `var.attrs`\n", + "* dimenstions: `var.dims`\n", + "* data type: `var.data.dtype`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a632a525", + "metadata": {}, + "outputs": [], + "source": [ + "var_name = \"time\"\n", + "var = ds[var_name]\n", + "\n", + "print(\"var.name: \\n\", var.name, \"\\n\")\n", + "print(\"var.data: \\n\", var.data, \"\\n\")\n", + "print(\"var.attrs: \\n\", var.attrs, \"\\n\")\n", + "print(\"var.dims: \\n\", var.dims, \"\\n\")\n", + "print(\"var.data.dtype: \\n\", var.data.dtype, \"\\n\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b256b07f", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: indivdual variable info table (collect and display variable info as a tabular format)\n", + "df_info = pd.DataFrame()\n", + "df_info[\"var_name\"] = list(ds.variables)\n", + "df_info[\"dims\"] = df_info.var_name.apply(lambda x: ds[x].dims)\n", + "df_info[\"is_dim\"] = df_info.var_name.apply(lambda x: x in ds.dims)\n", + "df_info[\"n_dim\"] = df_info.var_name.apply(lambda x: len(ds[x].dims))\n", + "df_info[\"attrs\"] = df_info.var_name.apply(lambda x: ds[x].attrs)\n", + "df_info[\"dtype\"] = df_info.var_name.apply(lambda x: ds[x].data.dtype)\n", + "df_info" + ] + }, + { + "cell_type": "markdown", + "id": "e6a36b96", + "metadata": {}, + "source": [ + "### Data cleaning/Preprocessing (skipped)\n", + "Data cleaning and preprocessing is an important stage in the the data analysis pipeline. However it will be out of the scope of this notebook. For more information about data cleaning and preprocessing basics, here are some references. \n", + "* [Xarray Fundamentals](https://earth-env-data-science.github.io/lectures/xarray/xarray.html)\n", + "* [Xarray Tutorial — Pangeo Gallery documentation](http://gallery.pangeo.io/repos/pangeo-data/pangeo-tutorial-gallery/xarray.html)\n", + "* [Pythonic Data Cleaning With pandas and NumPy](https://realpython.com/python-data-cleaning-numpy-pandas/)\n", + "* [Pandas - Cleaning Data](https://www.w3schools.com/python/pandas/pandas_cleaning.asp)" + ] + }, + { + "cell_type": "markdown", + "id": "ae6e1a69", + "metadata": {}, + "source": [ + "### Plotting\n", + "Data visualization (or plotting) is another important data analysis topic and deserves its own discussion. Here in this notebook we will only demonstrate basic tool for simple data visualization tasks.\n", + "\n", + "Here are some reference you might find useful:\n", + "* xarray plotting: https://docs.xarray.dev/en/stable/user-guide/plotting.html\n", + "* Atmospheric Community Toolkit (ACT): https://arm-doe.github.io/ACT/index.html\n", + "\n", + "Note: this notebook is auto-generated using a template. It uses a general idea to select variable(s) to plot and is not customized to each indivdual notebook. Feel free to change the variables in intrrests, especially certain figure is failed to plot." + ] + }, + { + "cell_type": "markdown", + "id": "7e417fb2", + "metadata": {}, + "source": [ + "#### 1-dimenssional basic time series plot\n", + "\n", + "For the following plot we would like to find variables such that\n", + "* it has one and only one dimession\n", + "* \"time\" is its coordinate variable\n", + "* it is not a dimenssion itself,\n", + "* it is not a special variable with substrings within [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "\n", + "Please see the following example in action. (For more details about pandas filtering, please see the following references.)\n", + "* [pandas: multiple conditions while indexing data frame](https://stackoverflow.com/questions/22591174/pandas-multiple-conditions-while-indexing-data-frame-unexpected-behavior)\n", + "* [pandas.DataFrame.apply](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.apply.html)\n", + "* [How to test if a string contains one of the substrings in a list...](https://stackoverflow.com/questions/26577516/how-to-test-if-a-string-contains-one-of-the-substrings-in-a-list-in-pandas" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30edac2d", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==1, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter = df_info[(df_info.n_dim == 1) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4777a659", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_1d = df_filter.var_name.values[0]\n", + " var_1d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb5ae985", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " ds[var_1d].plot()\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "9e41066e", + "metadata": {}, + "source": [ + "#### 2-dimenssional basic plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09bd4adc", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==2, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_2 = df_info[(df_info.n_dim == 2) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2cdea1a9", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_2d = df_filter_2.var_name.values[0]\n", + " var_2d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5f48879", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " print(ds[var_2d].dims)\n", + " # ds[var_2d].plot()\n", + "\n", + " # conventionally, use \"time\" as x-axis\n", + " ds[var_2d].plot(x=\"time\")\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "7554b2a6", + "metadata": {}, + "source": [ + "#### qc-plotting (optional)\n", + "\n", + "Note: act qc plotting has more strict requirement, one of them is the associated qc variable need to have \"flag_masks\" attributes. Which is added by using ds = act.io.armfiles.read_netcdf(files_list), then ds.clean.cleanup()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71c2096f", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " ds_act = act.io.armfiles.read_netcdf(full_path)\n", + " print(type(ds_act))\n", + " ds_act.clean.cleanup()\n", + "\n", + " # or \n", + " # ds.clean.cleanup()\n", + "except Exception as e:\n", + " print(\"ERROR\", e)\n", + " ds_act = ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "844f8505", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter valid variables for ACT qc plotting\n", + "condition = (\"qc_\" + df_info.var_name).apply(lambda x: ds_act[x].attrs.get(\"flag_masks\") is not None \n", + " if x in list(ds.variables) else False)\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_3 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings))) &\n", + " condition\n", + " ]\n", + "df_filter_3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd38cc6", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "try:\n", + " qc_variable = df_filter_3.var_name.values[0]\n", + " print(qc_variable)\n", + "except Exception as e:\n", + " print(e)\n", + " \n", + "try:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + " qc_ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "d76e4d27", + "metadata": {}, + "source": [ + "#### bonus: choose variables to plot from a dropdown menu " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb733804", + "metadata": {}, + "outputs": [], + "source": [ + "# Valid variables filtering\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_4 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82d5c20d", + "metadata": {}, + "outputs": [], + "source": [ + "# example 1: using xarray plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2fc9aaa4", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# fig, ax = plt.subplots(figsize=(10, 4))\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "# fig.clear() # Remove old lines from plot and plot new one\n", + "# if len(ds[var].dims)==2:\n", + "# ds[var].plot(x=\"time\", add_colorbar=False)\n", + "# else:\n", + "# ds[var].plot()\n", + "# plt.grid()\n", + "# plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fabdd802", + "metadata": {}, + "outputs": [], + "source": [ + "# example 2: using act plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "962b4186", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "\n", + "\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "\n", + "# i_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + "# i_display.add_subplots((1,), figsize=(10, 4))\n", + "# ax = i_display.plot(var, subplot_index=(0,), set_title=f\"{var} ({ds_act[var].attrs['long_name']})\",)\n", + "\n", + "# ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "# ax.grid()\n", + "# plt.show()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/SACRADV3D3C/kasacradv3d3c.c1.ipynb b/VAPs/quicklook/SACRADV3D3C/kasacradv3d3c.c1.ipynb new file mode 100644 index 00000000..681c8eee --- /dev/null +++ b/VAPs/quicklook/SACRADV3D3C/kasacradv3d3c.c1.ipynb @@ -0,0 +1,297 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# KASACRADV3D3C.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/sacradv3d3c) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'kasacradv3d3c'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2012-08-31', 'facility': 'C1', 'site': 'sgp', 'start_date': '2012-08-01'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2012-08-29'\n", + "date_end = '2012-08-31'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['wind_speed', 'wind_direction', 'cloud_fraction']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'wind_speed'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/SACRADVVAD/SACRADVVAD_tutorial.ipynb b/VAPs/quicklook/SACRADVVAD/SACRADVVAD_tutorial.ipynb new file mode 100644 index 00000000..c100b65a --- /dev/null +++ b/VAPs/quicklook/SACRADVVAD/SACRADVVAD_tutorial.ipynb @@ -0,0 +1,838 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# KASACRADVVAD.C1 Notebook\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/sacradvvad) for more information about this vap." + ] + }, + { + "cell_type": "markdown", + "id": "97097763", + "metadata": {}, + "source": [ + "In this notebook, we demonstrate the workflow to explore ARM vap data (using kasacradvvad as an example.) Value-added products (VAPs) are higher-order data products that have been analyzed and processed to ease scientists’ use of ARM data in atmospheric research and global climate models. \n", + "Here is the main content we will cover." + ] + }, + { + "cell_type": "markdown", + "id": "eddec40f", + "metadata": {}, + "source": [ + "# Table of Content\n", + "## Access the data\n", + "* How to retrieve the data\n", + "* Data path and file name conventions\n", + "* Load data\n", + "## Explore the data\n", + "* NetCDF Data structure\n", + "* Xarray essentials\n", + "* Xarray Variable\n", + "## Plot the data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "import random\n", + "\n", + "import glob\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "2372d5be", + "metadata": {}, + "source": [ + "## Access the data" + ] + }, + { + "cell_type": "markdown", + "id": "5b0d9684", + "metadata": {}, + "source": [ + "### How to retrieve the data\n", + "We assume the path \"/data/archive\" is available where you are running this notebook. You can use `os.path.exists(\"/data/archive\")` to verify if the path exists at your machine.\n", + "\n", + "\n", + "### Data path and file name conventions\n", + "There several common terminologies regarding ARM data, for example, data-stream-name, data-level, site, facility, instrument, etc. (For more details, please see [ARM Data File Standards Version 1.2](https://www.google.com/search?q=arm+datastream+facility+cite+definition&rlz=1C1GCEJ_enUS1029US1029&ei=hb41ZICrBPukqtsPvMODIA&ved=0ahUKEwjAgeiS0aL-AhV7kmoFHbzhAAQQ4dUDCBA&uact=5&oq=arm+datastream+facility+cite+definition&gs_lcp=Cgxnd3Mtd2l6LXNlcnAQAzIFCAAQogQyBQgAEKIEMgUIABCiBDIFCAAQogQyBQgAEKIEOgoIABBHENYEELADOgoIIRCgARDDBBAKSgQIQRgAUJ4DWIoOYO0PaAFwAXgAgAGMAYgBuQmSAQMyLjmYAQCgAQHIAQjAAQE&sclient=gws-wiz-serp).) \n", + "\n", + "For example, this notebook is called `kasacradvvad.c1`, where `kasacradvvad` is the \"datastream name\", and the `{process.ds_class_level}}` is the \"data level\".\n", + "\n", + "This datastream also contains site `pvc` and facility `M1`. (Note: individual datastream might have multiple site-facility pairs.)\n", + "In such a case, the data of this data-stream is stored at `/data/archive/pvc/pvckasacradvvadM1.c1`, which is in the format of `//.`. We can use the following method to assign the data-stream directory `datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )`\n", + "\n", + "The data files under datastream_dir also follows naming conventions. But once reach the datastream_dir level, the most import file naming convention to differentiate the files is \"yyyyMMdd.hhmmss\", which comes handy to filter out files based on datetime. For example, we can use `glob.glob(f'{datastream_dir}/*.200709*.*')` to filter files in 2007 September.\n", + "\n", + "(Note: refer to the https://adc.arm.gov/discovery/#/ and https://adc.arm.gov/solr8/metadata/select API to explore ARM datastream and assoicated available sites and facilities\n", + "\n", + "Please see the following examples in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d7e9eb85", + "metadata": {}, + "outputs": [], + "source": [ + "# Verify if DATA_DIR path exists\n", + "DATA_DIR = \"/data/archive\"\n", + "os.path.exists(DATA_DIR)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "586993fd", + "metadata": {}, + "outputs": [], + "source": [ + "# Speicify datastream_dir following the path conventions and check its existence\n", + "DATASTREAM_NAME = \"kasacradvvad\"\n", + "DATA_LEVEL = \"c1\"\n", + "site = \"pvc\"\n", + "facility = \"M1\"\n", + "datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )\n", + "print(datastream_dir)\n", + "print(os.path.exists(datastream_dir))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0742f7c1", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: list 5 (random) files under datastream_dir\n", + "files = os.listdir(datastream_dir)\n", + "files[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39b98a36", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: get most recent file\n", + "list_of_files = glob.glob(f\"{datastream_dir}/*\") # * means all if need specific format then *.csv\n", + "latest_file = max(list_of_files, key=os.path.getctime)\n", + "latest_file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "902d514e", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus sort datastream files based on datetime\n", + "files = os.listdir(datastream_dir)\n", + "file_sorted = files.copy()\n", + "file_sorted.sort() \n", + "print(file_sorted[:5])\n", + "\n", + "# to reverse\n", + "file_sorted_reverse = files.copy() \n", + "file_sorted_reverse.sort(reverse=True)\n", + "print(file_sorted_reverse[:5])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec5923b2", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: pattern matching\n", + "# filter the 200709** files under datastream_dir\n", + "files_filter = glob.glob(f'{datastream_dir}/*.200709*.*')\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c4a4aa18", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: use armnotebook_utils.file_filter (TODO) to filter files based on datastream info \n" + ] + }, + { + "cell_type": "markdown", + "id": "cfeb9efc", + "metadata": {}, + "source": [ + "### Load data\n", + "The arm data is stored in [NetCDF](https://en.wikipedia.org/wiki/NetCDF#:~:text=%22NetCDF%20(network%20Common%20Data%20Form,format%20for%20representing%20scientific%20data.) format. We can use xarray's `open_dataset` method to load single file, or `open_mfdataset` to open multiple files. (Note: the latter will still return a single xarray dataset object by combining multiple files.)\n", + "\n", + "Note: open_dataset keeps the file handle open and lazy loads its contents. All parameters are passed directly to open_dataset. It is a preferable method over load_dataset for memory-effiency. (more details, please see [xarray.open_dataset](https://docs.xarray.dev/en/stable/generated/xarray.open_dataset.html))\n", + "\n", + "See the following example in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a440a329", + "metadata": {}, + "outputs": [], + "source": [ + "# open single file\n", + "full_path = latest_file\n", + "print(full_path)\n", + "ds_single = xr.open_dataset(full_path)\n", + "ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0143a3d", + "metadata": {}, + "outputs": [], + "source": [ + "print(ds_single)\n", + "print(type(ds_single))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c0f8939", + "metadata": {}, + "outputs": [], + "source": [ + "# open multiple files\n", + "n_files = 3\n", + "full_paths = [os.path.join(datastream_dir, f_path) for f_path in file_sorted_reverse[:n_files]]\n", + "print(full_paths)\n", + "try: # Note: sometimes multiple files cannot be merged, so we used try except here.\n", + " ds_mutiple = xr.open_mfdataset(full_paths)\n", + " # ds_mutiple\n", + " print(type(ds_mutiple))\n", + " print(ds_mutiple)\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "3fa59943", + "metadata": {}, + "source": [ + "## Explore the data" + ] + }, + { + "cell_type": "markdown", + "id": "571f69b9", + "metadata": {}, + "source": [ + "### NetCDF Data structure \n", + "\n", + "\n", + "(If you are confident with NetCDF basics and xarray essentials, feel free to skip this session.)\n", + "Before we dive into data exploration, there are some eseential concpets we should be familiar with (shown below): \n", + "* Dataset\n", + "* Data array\n", + "* Variable\n", + "* Dimenssion\n", + "* Coordinate\n", + "* Data Type\n", + "* Meta Data (Attributes)\n", + "\n", + "We will not go into details about NetCDF basics and here are some references you might find helpful\n", + "* [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#)\n", + "* [Network Common Data Form (NetCDF)](https://www.unidata.ucar.edu/software/netcdf/) and [A Brief History of (netCDF) Time](https://www.unidata.ucar.edu/software/netcdf/time/recs.html)\n", + "\n", + "\n", + "\n", + "### Xarray essentials\n", + "Earlier we introduced the xarray pacakge and used `open_dataset` and `open_mfdataset` to retrieve NetCDF data file as an xarray Dataset object. Recall that you can review the data in a notebook by using `print(ds)` or `ds`. \n", + "\n", + "Using xarray to retrieve the aforementioned NetCDF basics is straightforwared. In general,\n", + "* Dataset: `ds`\n", + "* Data array: `ds.variables`\n", + "* Variable: `ds.ds.variables`\n", + "* Dimenssion: `ds.dims`\n", + "* Coordinate: `ds.coords`\n", + "* Data Type: `type`\n", + "* Meta Data (Attributes)\n", + "\n", + "Also, here are some references if you are new to xarray\n", + "* [Xarray in 45 minutes](https://tutorial.xarray.dev/overview/xarray-in-45-min.html)\n", + "* [Handling NetCDF Files using XArray for Absolute Beginners](https://towardsdatascience.com/handling-netcdf-files-using-xarray-for-absolute-beginners-111a8ab4463f)\n", + "\n", + "Try the following commands in action\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b3eac323", + "metadata": {}, + "outputs": [], + "source": [ + "ds = ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77ecf85d", + "metadata": {}, + "outputs": [], + "source": [ + "# Dataset \n", + "ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25e7de09", + "metadata": {}, + "outputs": [], + "source": [ + "# Data array, Variable\n", + "# Note: the info can be overwhelming. Do not attempt to grasp everything at the first glance.\n", + "ds.variables" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c41b67e", + "metadata": {}, + "outputs": [], + "source": [ + "# dimenssions\n", + "ds.dims" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "156f1dfc", + "metadata": {}, + "outputs": [], + "source": [ + "# coordinates\n", + "ds.coords" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "277d6064", + "metadata": {}, + "outputs": [], + "source": [ + "# Meta Data (Attributes)\n", + "ds.attrs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d334681f", + "metadata": {}, + "outputs": [], + "source": [ + "# type\n", + "print(type(ds))\n", + "print(type(ds.variables))\n", + "print(type(ds.dims))\n", + "print(type(ds.coords))\n", + "print(type(ds.attrs))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "643399d6", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: retrieve variable names only\n", + "list(ds.variables)" + ] + }, + { + "cell_type": "markdown", + "id": "1cf36878", + "metadata": {}, + "source": [ + "#### Discussion: variable vs. coordinates vs. dimenssions. \n", + "From [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#):\n", + "\n", + "* Dimession: A dimension may be used to represent a real physical dimension, for example, time, latitude, longitude, or height. A dimension might also be used to index other quantities, for example station or model-run-number.\n", + "A netCDF dimension has both a name and a length. A dimension length is an arbitrary positive integer, except that one dimension in a netCDF dataset can have the length UNLIMITED.\n", + "\n", + "* Variables: Variables are used to store the bulk of the data in a netCDF dataset. A variable represents an array of values of the same type. A scalar value is treated as a 0-dimensional array. A variable has a name, a data type, and a shape described by its list of dimensions specified when the variable is created. A variable may also have associated attributes, which may be added, deleted or changed after the variable is created.\n", + "\n", + "* Coordinate (Variables): It is legal for a variable to have the same name as a dimension. Such variables have no special meaning to the netCDF library. However there is a convention that such variables should be treated in a special way by software using this library.\n", + "A variable with the same name as a dimension is called a coordinate variable. It typically defines a physical coordinate corresponding to that dimension.\n", + "\n", + "\n", + "Tips: By definition, coordinates and dimenssions are also variable. For example ds.time is a coordinate but it is also a special variable. But in practice, when we talk about variable, it implies regular/non-coordinate variable. We will use this convention for the remaining notebook. " + ] + }, + { + "cell_type": "markdown", + "id": "d47120f9", + "metadata": {}, + "source": [ + "### Xarray Variable\n", + "\n", + "\n", + "We can use `ds.variables` to access varialbes of a dataset. But many times viewing print-out the whole datasets can be overwhemling. Instead, we would work on the individual variable (also called Xarray data array.) using the `ds[var_name]` syntax, where `var_name` is the name of the variable. See the following examples in action.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5a57e136", + "metadata": {}, + "outputs": [], + "source": [ + "print(type(ds[\"time\"]))\n", + "ds[\"time\"]" + ] + }, + { + "cell_type": "markdown", + "id": "51ec643e", + "metadata": {}, + "source": [ + "#### Variable properties\n", + "For individual varible, the following properties are mostly used with the assocaited xarray method to retrieve them. (assuming var = ds[var_name])\n", + "* name: `var.name`\n", + "* data content: `var.data`\n", + "* attributes: `var.attrs`\n", + "* dimenstions: `var.dims`\n", + "* data type: `var.data.dtype`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a632a525", + "metadata": {}, + "outputs": [], + "source": [ + "var_name = \"time\"\n", + "var = ds[var_name]\n", + "\n", + "print(\"var.name: \\n\", var.name, \"\\n\")\n", + "print(\"var.data: \\n\", var.data, \"\\n\")\n", + "print(\"var.attrs: \\n\", var.attrs, \"\\n\")\n", + "print(\"var.dims: \\n\", var.dims, \"\\n\")\n", + "print(\"var.data.dtype: \\n\", var.data.dtype, \"\\n\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b256b07f", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: indivdual variable info table (collect and display variable info as a tabular format)\n", + "df_info = pd.DataFrame()\n", + "df_info[\"var_name\"] = list(ds.variables)\n", + "df_info[\"dims\"] = df_info.var_name.apply(lambda x: ds[x].dims)\n", + "df_info[\"is_dim\"] = df_info.var_name.apply(lambda x: x in ds.dims)\n", + "df_info[\"n_dim\"] = df_info.var_name.apply(lambda x: len(ds[x].dims))\n", + "df_info[\"attrs\"] = df_info.var_name.apply(lambda x: ds[x].attrs)\n", + "df_info[\"dtype\"] = df_info.var_name.apply(lambda x: ds[x].data.dtype)\n", + "df_info" + ] + }, + { + "cell_type": "markdown", + "id": "e6a36b96", + "metadata": {}, + "source": [ + "### Data cleaning/Preprocessing (skipped)\n", + "Data cleaning and preprocessing is an important stage in the the data analysis pipeline. However it will be out of the scope of this notebook. For more information about data cleaning and preprocessing basics, here are some references. \n", + "* [Xarray Fundamentals](https://earth-env-data-science.github.io/lectures/xarray/xarray.html)\n", + "* [Xarray Tutorial — Pangeo Gallery documentation](http://gallery.pangeo.io/repos/pangeo-data/pangeo-tutorial-gallery/xarray.html)\n", + "* [Pythonic Data Cleaning With pandas and NumPy](https://realpython.com/python-data-cleaning-numpy-pandas/)\n", + "* [Pandas - Cleaning Data](https://www.w3schools.com/python/pandas/pandas_cleaning.asp)" + ] + }, + { + "cell_type": "markdown", + "id": "ae6e1a69", + "metadata": {}, + "source": [ + "### Plotting\n", + "Data visualization (or plotting) is another important data analysis topic and deserves its own discussion. Here in this notebook we will only demonstrate basic tool for simple data visualization tasks.\n", + "\n", + "Here are some reference you might find useful:\n", + "* xarray plotting: https://docs.xarray.dev/en/stable/user-guide/plotting.html\n", + "* Atmospheric Community Toolkit (ACT): https://arm-doe.github.io/ACT/index.html\n", + "\n", + "Note: this notebook is auto-generated using a template. It uses a general idea to select variable(s) to plot and is not customized to each indivdual notebook. Feel free to change the variables in intrrests, especially certain figure is failed to plot." + ] + }, + { + "cell_type": "markdown", + "id": "7e417fb2", + "metadata": {}, + "source": [ + "#### 1-dimenssional basic time series plot\n", + "\n", + "For the following plot we would like to find variables such that\n", + "* it has one and only one dimession\n", + "* \"time\" is its coordinate variable\n", + "* it is not a dimenssion itself,\n", + "* it is not a special variable with substrings within [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "\n", + "Please see the following example in action. (For more details about pandas filtering, please see the following references.)\n", + "* [pandas: multiple conditions while indexing data frame](https://stackoverflow.com/questions/22591174/pandas-multiple-conditions-while-indexing-data-frame-unexpected-behavior)\n", + "* [pandas.DataFrame.apply](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.apply.html)\n", + "* [How to test if a string contains one of the substrings in a list...](https://stackoverflow.com/questions/26577516/how-to-test-if-a-string-contains-one-of-the-substrings-in-a-list-in-pandas" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30edac2d", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==1, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter = df_info[(df_info.n_dim == 1) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4777a659", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_1d = df_filter.var_name.values[0]\n", + " var_1d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb5ae985", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " ds[var_1d].plot()\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "9e41066e", + "metadata": {}, + "source": [ + "#### 2-dimenssional basic plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09bd4adc", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==2, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_2 = df_info[(df_info.n_dim == 2) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2cdea1a9", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_2d = df_filter_2.var_name.values[0]\n", + " var_2d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5f48879", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " print(ds[var_2d].dims)\n", + " # ds[var_2d].plot()\n", + "\n", + " # conventionally, use \"time\" as x-axis\n", + " ds[var_2d].plot(x=\"time\")\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "7554b2a6", + "metadata": {}, + "source": [ + "#### qc-plotting (optional)\n", + "\n", + "Note: act qc plotting has more strict requirement, one of them is the associated qc variable need to have \"flag_masks\" attributes. Which is added by using ds = act.io.armfiles.read_netcdf(files_list), then ds.clean.cleanup()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71c2096f", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " ds_act = act.io.armfiles.read_netcdf(full_path)\n", + " print(type(ds_act))\n", + " ds_act.clean.cleanup()\n", + "\n", + " # or \n", + " # ds.clean.cleanup()\n", + "except Exception as e:\n", + " print(\"ERROR\", e)\n", + " ds_act = ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "844f8505", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter valid variables for ACT qc plotting\n", + "condition = (\"qc_\" + df_info.var_name).apply(lambda x: ds_act[x].attrs.get(\"flag_masks\") is not None \n", + " if x in list(ds.variables) else False)\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_3 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings))) &\n", + " condition\n", + " ]\n", + "df_filter_3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd38cc6", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "try:\n", + " qc_variable = df_filter_3.var_name.values[0]\n", + " print(qc_variable)\n", + "except Exception as e:\n", + " print(e)\n", + " \n", + "try:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + " qc_ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "d76e4d27", + "metadata": {}, + "source": [ + "#### bonus: choose variables to plot from a dropdown menu " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb733804", + "metadata": {}, + "outputs": [], + "source": [ + "# Valid variables filtering\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_4 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82d5c20d", + "metadata": {}, + "outputs": [], + "source": [ + "# example 1: using xarray plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2fc9aaa4", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# fig, ax = plt.subplots(figsize=(10, 4))\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "# fig.clear() # Remove old lines from plot and plot new one\n", + "# if len(ds[var].dims)==2:\n", + "# ds[var].plot(x=\"time\", add_colorbar=False)\n", + "# else:\n", + "# ds[var].plot()\n", + "# plt.grid()\n", + "# plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fabdd802", + "metadata": {}, + "outputs": [], + "source": [ + "# example 2: using act plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "962b4186", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "\n", + "\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "\n", + "# i_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + "# i_display.add_subplots((1,), figsize=(10, 4))\n", + "# ax = i_display.plot(var, subplot_index=(0,), set_title=f\"{var} ({ds_act[var].attrs['long_name']})\",)\n", + "\n", + "# ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "# ax.grid()\n", + "# plt.show()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/SACRADVVAD/kasacradvvad.c1.ipynb b/VAPs/quicklook/SACRADVVAD/kasacradvvad.c1.ipynb new file mode 100644 index 00000000..2ff9b73f --- /dev/null +++ b/VAPs/quicklook/SACRADVVAD/kasacradvvad.c1.ipynb @@ -0,0 +1,297 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# KASACRADVVAD.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/sacradvvad) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'kasacradvvad'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2012-09-30', 'facility': 'M1', 'site': 'pvc', 'start_date': '2012-09-01'}, {'end_date': '2012-08-31', 'facility': 'C1', 'site': 'sgp', 'start_date': '2012-08-01'}, {'end_date': '2014-08-30', 'facility': 'M1', 'site': 'tmp', 'start_date': '2014-08-01'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2012-08-29'\n", + "date_end = '2012-08-31'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['horizontal_wind_magnitude_at_cloud_level', 'horizontal_wind_direction_at_cloud_level']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'horizontal_wind_magnitude_at_cloud_level'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/SFCCLDGRID/.ipynb_checkpoints/15swfcldgrid1long.c1-checkpoint.ipynb b/VAPs/quicklook/SFCCLDGRID/.ipynb_checkpoints/15swfcldgrid1long.c1-checkpoint.ipynb new file mode 100644 index 00000000..0ed9cccf --- /dev/null +++ b/VAPs/quicklook/SFCCLDGRID/.ipynb_checkpoints/15swfcldgrid1long.c1-checkpoint.ipynb @@ -0,0 +1,2384 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# 15SWFCLDGRID1LONG.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/sfccldgrid) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = '15swfcldgrid1long'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2009-11-25', 'facility': 'N1', 'site': 'sgp', 'start_date': '1997-01-01'}]" + ] + }, + { + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "ac6764f5", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The following locations and date ranges are available for this VAP:\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
sitefacilitystart_dateend_date
0sgpN11997-01-012009-11-25
\n", + "
" + ], + "text/plain": [ + " site facility start_date end_date\n", + "0 sgp N1 1997-01-01 2009-11-25" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'N1' )\n", + "\n", + "date_start = '2009-11-23'\n", + "date_end = '2009-11-25'" + ] + }, + { + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'/data/archive/sgp/sgp15swfcldgrid1longN1.c1'" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['20091123', '20091124', '20091125']" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "51feea2e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['/data/archive/sgp/sgp15swfcldgrid1longN1.c1/sgp15swfcldgrid1longN1.c1.20091123.180000.cdf',\n", + " '/data/archive/sgp/sgp15swfcldgrid1longN1.c1/sgp15swfcldgrid1longN1.c1.20091124.144500.cdf',\n", + " '/data/archive/sgp/sgp15swfcldgrid1longN1.c1/sgp15swfcldgrid1longN1.c1.20091125.144500.cdf']" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "3 files loaded\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
<xarray.Dataset>\n",
+       "Dimensions:           (time: 33, lat: 15, lon: 17)\n",
+       "Coordinates:\n",
+       "  * time              (time) datetime64[ns] 2009-11-23T18:00:00 ... 2009-11-2...\n",
+       "  * lat               (lat) float32 38.5 38.25 38.0 37.75 ... 35.5 35.25 35.0\n",
+       "  * lon               (lon) float32 99.5 99.25 99.0 98.75 ... 96.0 95.75 95.5\n",
+       "Data variables: (12/22)\n",
+       "    base_time         (time) datetime64[ns] 2009-11-23T18:00:00 ... 2009-11-2...\n",
+       "    time_offset       (time) datetime64[ns] 2009-11-23T18:00:00 ... 2009-11-2...\n",
+       "    cloudfraction     (time, lat, lon) float32 dask.array<chunksize=(2, 15, 17), meta=np.ndarray>\n",
+       "    qc_cloudfraction  (time, lat, lon) int32 dask.array<chunksize=(2, 15, 17), meta=np.ndarray>\n",
+       "    cf_cloudfraction  (time) int32 dask.array<chunksize=(2,), meta=np.ndarray>\n",
+       "    tswfluxdn         (time, lat, lon) float32 dask.array<chunksize=(2, 15, 17), meta=np.ndarray>\n",
+       "    ...                ...\n",
+       "    cf_clrfluxdn      (time) int32 dask.array<chunksize=(2,), meta=np.ndarray>\n",
+       "    cdirfluxdn        (time, lat, lon) float32 dask.array<chunksize=(2, 15, 17), meta=np.ndarray>\n",
+       "    qc_cdirfluxdn     (time, lat, lon) int32 dask.array<chunksize=(2, 15, 17), meta=np.ndarray>\n",
+       "    cf_cdirfluxdn     (time) int32 dask.array<chunksize=(2,), meta=np.ndarray>\n",
+       "    azimuth           (time) float32 dask.array<chunksize=(2,), meta=np.ndarray>\n",
+       "    alt               (time) float32 318.0 318.0 318.0 ... 318.0 318.0 318.0\n",
+       "Attributes: (12/18)\n",
+       "    Date:                           Wed Jun 16 21:58:59 2010\n",
+       "    Version:                        $State: process-vap-sfccldgrid1long-2.0-0 $\n",
+       "    Command_Line:                   sfccldgrid1long -d 20091123\n",
+       "    Input_Platforms:                sgp15swfanalbrs1longC1.c1, sgp15swfanalsi...\n",
+       "    BW_Version:                     $State: ds-dsutil-bw-4.3-0 $\n",
+       "    qc_format_version:              0.1\n",
+       "    ...                             ...\n",
+       "    history:                        created by user dsmgr on machine zinc at ...\n",
+       "    _file_dates:                    ['20091123', '20091124', '20091125']\n",
+       "    _file_times:                    ['180000', '144500', '144500']\n",
+       "    datastream:                     sgp15swfcldgrid1longN1.c1\n",
+       "    _datastream:                    sgp15swfcldgrid1longN1.c1\n",
+       "    _arm_standards_flag:            1
" + ], + "text/plain": [ + "\n", + "Dimensions: (time: 33, lat: 15, lon: 17)\n", + "Coordinates:\n", + " * time (time) datetime64[ns] 2009-11-23T18:00:00 ... 2009-11-2...\n", + " * lat (lat) float32 38.5 38.25 38.0 37.75 ... 35.5 35.25 35.0\n", + " * lon (lon) float32 99.5 99.25 99.0 98.75 ... 96.0 95.75 95.5\n", + "Data variables: (12/22)\n", + " base_time (time) datetime64[ns] 2009-11-23T18:00:00 ... 2009-11-2...\n", + " time_offset (time) datetime64[ns] 2009-11-23T18:00:00 ... 2009-11-2...\n", + " cloudfraction (time, lat, lon) float32 dask.array\n", + " qc_cloudfraction (time, lat, lon) int32 dask.array\n", + " cf_cloudfraction (time) int32 dask.array\n", + " tswfluxdn (time, lat, lon) float32 dask.array\n", + " ... ...\n", + " cf_clrfluxdn (time) int32 dask.array\n", + " cdirfluxdn (time, lat, lon) float32 dask.array\n", + " qc_cdirfluxdn (time, lat, lon) int32 dask.array\n", + " cf_cdirfluxdn (time) int32 dask.array\n", + " azimuth (time) float32 dask.array\n", + " alt (time) float32 318.0 318.0 318.0 ... 318.0 318.0 318.0\n", + "Attributes: (12/18)\n", + " Date: Wed Jun 16 21:58:59 2010\n", + " Version: $State: process-vap-sfccldgrid1long-2.0-0 $\n", + " Command_Line: sfccldgrid1long -d 20091123\n", + " Input_Platforms: sgp15swfanalbrs1longC1.c1, sgp15swfanalsi...\n", + " BW_Version: $State: ds-dsutil-bw-4.3-0 $\n", + " qc_format_version: 0.1\n", + " ... ...\n", + " history: created by user dsmgr on machine zinc at ...\n", + " _file_dates: ['20091123', '20091124', '20091125']\n", + " _file_times: ['180000', '144500', '144500']\n", + " datastream: sgp15swfcldgrid1longN1.c1\n", + " _datastream: sgp15swfcldgrid1longN1.c1\n", + " _arm_standards_flag: 1" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['cloudfraction', 'cf_cloudfraction', 'tswfluxdn']" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [ + { + "ename": "TypeError", + "evalue": "Dimensions of C (17, 15, 33) should be one smaller than X(33) and Y(15) while using shading='flat' see help(pcolormesh)", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[0;32mIn[9], line 5\u001b[0m\n\u001b[1;32m 2\u001b[0m ts_display\u001b[38;5;241m.\u001b[39madd_subplots((\u001b[38;5;28mlen\u001b[39m(variables_to_plot),), figsize \u001b[38;5;241m=\u001b[39m (\u001b[38;5;241m9.5\u001b[39m,\u001b[38;5;241m4\u001b[39m\u001b[38;5;241m*\u001b[39m\u001b[38;5;28mlen\u001b[39m(variables_to_plot)))\n\u001b[1;32m 4\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m i,v \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28menumerate\u001b[39m(variables_to_plot):\n\u001b[0;32m----> 5\u001b[0m ts_ax \u001b[38;5;241m=\u001b[39m \u001b[43mts_display\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mplot\u001b[49m\u001b[43m(\u001b[49m\u001b[43mv\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43msubplot_index\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mi\u001b[49m\u001b[43m,\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mset_title\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mds\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mvariables\u001b[49m\u001b[43m[\u001b[49m\u001b[43mv\u001b[49m\u001b[43m]\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mattrs\u001b[49m\u001b[43m[\u001b[49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43mlong_name\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m,\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 6\u001b[0m ts_ax\u001b[38;5;241m.\u001b[39mgrid()\n\u001b[1;32m 8\u001b[0m plt\u001b[38;5;241m.\u001b[39mshow()\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/act/plotting/timeseriesdisplay.py:588\u001b[0m, in \u001b[0;36mTimeSeriesDisplay.plot\u001b[0;34m(self, field, dsname, subplot_index, cmap, set_title, add_nan, day_night_background, invert_y_axis, abs_limits, time_rng, y_rng, use_var_for_y, set_shading, assessment_overplot, overplot_marker, overplot_behind, overplot_markersize, assessment_overplot_category, assessment_overplot_category_color, force_line_plot, labels, cbar_label, cbar_h_adjust, secondary_y, y_axis_flag_meanings, colorbar_labels, cb_friendly, **kwargs)\u001b[0m\n\u001b[1;32m 586\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124medgecolors\u001b[39m\u001b[38;5;124m'\u001b[39m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;129;01min\u001b[39;00m kwargs\u001b[38;5;241m.\u001b[39mkeys():\n\u001b[1;32m 587\u001b[0m kwargs[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124medgecolors\u001b[39m\u001b[38;5;124m'\u001b[39m] \u001b[38;5;241m=\u001b[39m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mface\u001b[39m\u001b[38;5;124m'\u001b[39m\n\u001b[0;32m--> 588\u001b[0m mesh \u001b[38;5;241m=\u001b[39m \u001b[43max\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mpcolormesh\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 589\u001b[0m \u001b[43m \u001b[49m\u001b[43mnp\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43masarray\u001b[49m\u001b[43m(\u001b[49m\u001b[43mxdata\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 590\u001b[0m \u001b[43m \u001b[49m\u001b[43mydata\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 591\u001b[0m \u001b[43m \u001b[49m\u001b[43mdata\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mtranspose\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 592\u001b[0m \u001b[43m \u001b[49m\u001b[43mshading\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mset_shading\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 593\u001b[0m \u001b[43m \u001b[49m\u001b[43mcmap\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcmap\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 594\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 595\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 597\u001b[0m \u001b[38;5;66;03m# Set Title\u001b[39;00m\n\u001b[1;32m 598\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m set_title \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/matplotlib/__init__.py:1442\u001b[0m, in \u001b[0;36m_preprocess_data..inner\u001b[0;34m(ax, data, *args, **kwargs)\u001b[0m\n\u001b[1;32m 1439\u001b[0m \u001b[38;5;129m@functools\u001b[39m\u001b[38;5;241m.\u001b[39mwraps(func)\n\u001b[1;32m 1440\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21minner\u001b[39m(ax, \u001b[38;5;241m*\u001b[39margs, data\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mNone\u001b[39;00m, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs):\n\u001b[1;32m 1441\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m data \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[0;32m-> 1442\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mfunc\u001b[49m\u001b[43m(\u001b[49m\u001b[43max\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;28;43mmap\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43msanitize_sequence\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43margs\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 1444\u001b[0m bound \u001b[38;5;241m=\u001b[39m new_sig\u001b[38;5;241m.\u001b[39mbind(ax, \u001b[38;5;241m*\u001b[39margs, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs)\n\u001b[1;32m 1445\u001b[0m auto_label \u001b[38;5;241m=\u001b[39m (bound\u001b[38;5;241m.\u001b[39marguments\u001b[38;5;241m.\u001b[39mget(label_namer)\n\u001b[1;32m 1446\u001b[0m \u001b[38;5;129;01mor\u001b[39;00m bound\u001b[38;5;241m.\u001b[39mkwargs\u001b[38;5;241m.\u001b[39mget(label_namer))\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/matplotlib/axes/_axes.py:6220\u001b[0m, in \u001b[0;36mAxes.pcolormesh\u001b[0;34m(self, alpha, norm, cmap, vmin, vmax, shading, antialiased, *args, **kwargs)\u001b[0m\n\u001b[1;32m 6217\u001b[0m shading \u001b[38;5;241m=\u001b[39m shading\u001b[38;5;241m.\u001b[39mlower()\n\u001b[1;32m 6218\u001b[0m kwargs\u001b[38;5;241m.\u001b[39msetdefault(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124medgecolors\u001b[39m\u001b[38;5;124m'\u001b[39m, \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mnone\u001b[39m\u001b[38;5;124m'\u001b[39m)\n\u001b[0;32m-> 6220\u001b[0m X, Y, C, shading \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_pcolorargs\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43mpcolormesh\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 6221\u001b[0m \u001b[43m \u001b[49m\u001b[43mshading\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mshading\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mkwargs\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 6222\u001b[0m coords \u001b[38;5;241m=\u001b[39m np\u001b[38;5;241m.\u001b[39mstack([X, Y], axis\u001b[38;5;241m=\u001b[39m\u001b[38;5;241m-\u001b[39m\u001b[38;5;241m1\u001b[39m)\n\u001b[1;32m 6223\u001b[0m \u001b[38;5;66;03m# convert to one dimensional array, except for 3D RGB(A) arrays\u001b[39;00m\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/matplotlib/axes/_axes.py:5751\u001b[0m, in \u001b[0;36mAxes._pcolorargs\u001b[0;34m(self, funcname, shading, *args, **kwargs)\u001b[0m\n\u001b[1;32m 5749\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m shading \u001b[38;5;241m==\u001b[39m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mflat\u001b[39m\u001b[38;5;124m'\u001b[39m:\n\u001b[1;32m 5750\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m (Nx, Ny) \u001b[38;5;241m!=\u001b[39m (ncols \u001b[38;5;241m+\u001b[39m \u001b[38;5;241m1\u001b[39m, nrows \u001b[38;5;241m+\u001b[39m \u001b[38;5;241m1\u001b[39m):\n\u001b[0;32m-> 5751\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mTypeError\u001b[39;00m(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mDimensions of C \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mC\u001b[38;5;241m.\u001b[39mshape\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m should\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 5752\u001b[0m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m be one smaller than X(\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mNx\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m) and Y(\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mNy\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m)\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 5753\u001b[0m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m while using shading=\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mflat\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 5754\u001b[0m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m see help(\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mfuncname\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m)\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[1;32m 5755\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m: \u001b[38;5;66;03m# ['nearest', 'gouraud']:\u001b[39;00m\n\u001b[1;32m 5756\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m (Nx, Ny) \u001b[38;5;241m!=\u001b[39m (ncols, nrows):\n", + "\u001b[0;31mTypeError\u001b[0m: Dimensions of C (17, 15, 33) should be one smaller than X(33) and Y(15) while using shading='flat' see help(pcolormesh)" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "5ec59307f5704277961c72d61c88ebc6", + "version_major": 2, + "version_minor": 0 + }, + "image/png": "iVBORw0KGgoAAAANSUhEUgAAA7YAAASwCAYAAADPBNYLAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAA9hAAAPYQGoP6dpAABi0UlEQVR4nO3df3RV5Z3o/08gkKA1sUAJIIjRaqXlqkO4IrF8Ha3GQceWGeeKY5eog/c2Vy0DqV5FZvmD5axMO6tO6w9Qr6DjXWgz/hxmbkbNzFhFwRlJg+MIrb1CDWgiTRwT1DYI7O8fLjJNE5Rf5yQPvF5rnT/Ow7OTZ283cb/Z5+QUZFmWBQAAACRqUH8vAAAAAPaHsAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAnbHHvhhRfiggsuiLFjx0ZBQUE89dRTn7nN888/HxUVFVFcXBzHHnts3HPPPblfKAAAQKKEbY59+OGHcfLJJ8ddd921R/M3btwY5513XkyfPj2amprixhtvjLlz58bjjz+e45UCAACkqSDLsqy/F3GoKCgoiCeffDJmzpy52znXX399rFixItavX989Vl1dHa+++mqsXr06D6sEAABIS2F/L4CeVq9eHVVVVT3Gzj333Fi6dGl8/PHHMWTIkD636+rqiq6uru7nO3fujPfeey9GjBgRBQUFOV0zAAAc6rIsi61bt8bYsWNj0CAvjM03YTvAtLa2RllZWY+xsrKy2L59e7S1tcWYMWP63K62tjZuvfXWfCwRAADYjU2bNsW4ceP6exmHHGE7AP32HdZdrxb/tDuvCxYsiJqamu7nHR0dcfTRR8emTZuipKQkNwsFAAAiIqKzszPGjx8fRxxxRH8v5ZAkbAeY0aNHR2tra4+xLVu2RGFhYYwYMWK32xUVFUVRUVGv8ZKSEmELAAB54m2A/cOLvweYadOmRUNDQ4+xZ599NqZMmbLb99cCAAAcyoRtjn3wwQexdu3aWLt2bUR88nE+a9eujebm5oj45CXEs2fP7p5fXV0db731VtTU1MT69etj2bJlsXTp0rj22mv7Y/kAAAADnpci59iaNWvizDPP7H6+632wl112WTz44IPR0tLSHbkREeXl5VFfXx/z58+Pu+++O8aOHRt33HFHXHjhhXlfOwAAQAp8ju1BqrOzM0pLS6Ojo8N7bAEAIMdcf/cvL0UGAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbPNk8eLFUV5eHsXFxVFRURErV6781PnLly+Pk08+OQ477LAYM2ZMXHHFFdHe3p6n1QIAAKRD2OZBXV1dzJs3LxYuXBhNTU0xffr0mDFjRjQ3N/c5/8UXX4zZs2fHnDlz4vXXX49HH300XnnllbjyyivzvHIAAICBT9jmwe233x5z5syJK6+8MiZOnBg/+MEPYvz48bFkyZI+57/88stxzDHHxNy5c6O8vDy++tWvxre+9a1Ys2ZNnlcOAAAw8AnbHNu2bVs0NjZGVVVVj/GqqqpYtWpVn9tUVlbG5s2bo76+PrIsi3fffTcee+yxOP/883f7fbq6uqKzs7PHAwAA4FAgbHOsra0tduzYEWVlZT3Gy8rKorW1tc9tKisrY/ny5TFr1qwYOnRojB49Oo488si48847d/t9amtro7S0tPsxfvz4A7ofAAAAA5WwzZOCgoIez7Ms6zW2y7p162Lu3Llx0003RWNjYzz99NOxcePGqK6u3u3XX7BgQXR0dHQ/Nm3adEDXDwAAMFAV9vcCDnYjR46MwYMH97o7u2XLll53cXepra2N008/Pa677rqIiDjppJPi8MMPj+nTp8dtt90WY8aM6bVNUVFRFBUVHfgdAAAAGODcsc2xoUOHRkVFRTQ0NPQYb2hoiMrKyj63+eijj2LQoJ7/aQYPHhwRn9zpBQAA4D8J2zyoqamJ+++/P5YtWxbr16+P+fPnR3Nzc/dLixcsWBCzZ8/unn/BBRfEE088EUuWLIkNGzbESy+9FHPnzo1TTz01xo4d21+7AQAAMCB5KXIezJo1K9rb22PRokXR0tISkyZNivr6+pgwYUJERLS0tPT4TNvLL788tm7dGnfddVd85zvfiSOPPDLOOuus+O53v9tfuwAAADBgFWRe23pQ6uzsjNLS0ujo6IiSkpL+Xg4AABzUXH/3Ly9FBgAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmzzZPHixVFeXh7FxcVRUVERK1eu/NT5XV1dsXDhwpgwYUIUFRXFcccdF8uWLcvTagEAANJR2N8LOBTU1dXFvHnzYvHixXH66afHvffeGzNmzIh169bF0Ucf3ec2F110Ubz77ruxdOnS+OIXvxhbtmyJ7du353nlAAAAA19BlmVZfy/iYDd16tSYPHlyLFmypHts4sSJMXPmzKitre01/+mnn46LL744NmzYEMOHD9+n79nZ2RmlpaXR0dERJSUl+7x2AADgs7n+7l9eipxj27Zti8bGxqiqquoxXlVVFatWrepzmxUrVsSUKVPie9/7Xhx11FFxwgknxLXXXhu/+tWv8rFkAACApHgpco61tbXFjh07oqysrMd4WVlZtLa29rnNhg0b4sUXX4zi4uJ48skno62tLa666qp47733dvs+266urujq6up+3tnZeeB2AgAAYABzxzZPCgoKejzPsqzX2C47d+6MgoKCWL58eZx66qlx3nnnxe233x4PPvjgbu/a1tbWRmlpafdj/PjxB3wfAAAABiJhm2MjR46MwYMH97o7u2XLll53cXcZM2ZMHHXUUVFaWto9NnHixMiyLDZv3tznNgsWLIiOjo7ux6ZNmw7cTgAAAAxgwjbHhg4dGhUVFdHQ0NBjvKGhISorK/vc5vTTT4933nknPvjgg+6xN954IwYNGhTjxo3rc5uioqIoKSnp8QAAADgUCNs8qKmpifvvvz+WLVsW69evj/nz50dzc3NUV1dHxCd3W2fPnt09/5JLLokRI0bEFVdcEevWrYsXXnghrrvuuviTP/mTGDZsWH/tBgAAwIDkl0fl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pEmTor6+PiZMmBARES0tLT0+0/bee++N7du3x9VXXx1XX3119/hll10WDz74YL6XDwAAMKD5HNuDlM/RAgCA/HH93b+8xxYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwzZPFixdHeXl5FBcXR0VFRaxcufJT5z///PNRUVERxcXFceyxx8Y999yTp5UCAACkRdjmQV1dXcybNy8WLlwYTU1NMX369JgxY0Y0Nzf3OX/jxo1x3nnnxfTp06OpqSluvPHGmDt3bjz++ON5XjkAAMDAV5BlWdbfizjYTZ06NSZPnhxLlizpHps4cWLMnDkzamtre82//vrrY8WKFbF+/fruserq6nj11Vdj9erVe/Q9Ozs7o7S0NDo6OqKkpGT/dwIAANgt19/9yx3bHNu2bVs0NjZGVVVVj/GqqqpYtWpVn9usXr261/xzzz031qxZEx9//HHO1goAAJCiwv5ewMGura0tduzYEWVlZT3Gy8rKorW1tc9tWltb+5y/ffv2aGtrizFjxvTapqurK7q6urqfd3R0RMQn/3IEAADk1q7rbi+I7R/CNk8KCgp6PM+yrNfYZ83va3yX2trauPXWW3uNjx8/fm+XCgAA7KP29vYoLS3t72UccoRtjo0cOTIGDx7c6+7sli1bet2V3WX06NF9zi8sLIwRI0b0uc2CBQuipqam+/n7778fEyZMiObmZn+xPkVnZ2eMHz8+Nm3a5L0Qu+EY7RnH6bM5RnvGcdozjtNnc4z2jOP02RyjPdPR0RFHH310DB8+vL+XckgStjk2dOjQqKioiIaGhviDP/iD7vGGhob4xje+0ec206ZNi7/7u7/rMfbss8/GlClTYsiQIX1uU1RUFEVFRb3GS0tL/QDaAyUlJY7TZ3CM9ozj9Nkcoz3jOO0Zx+mzOUZ7xnH6bI7Rnhk0yK8x6g+Oeh7U1NTE/fffH8uWLYv169fH/Pnzo7m5OaqrqyPik7uts2fP7p5fXV0db731VtTU1MT69etj2bJlsXTp0rj22mv7axcAAAAGLHds82DWrFnR3t4eixYtipaWlpg0aVLU19fHhAkTIiKipaWlx2falpeXR319fcyfPz/uvvvuGDt2bNxxxx1x4YUX9tcuAAAADFjCNk+uuuqquOqqq/r8swcffLDX2BlnnBE/+clP9vn7FRUVxc0339zny5P5T47TZ3OM9ozj9Nkcoz3jOO0Zx+mzOUZ7xnH6bI7RnnGc+ldB5vdRAwAAkDDvsQUAACBpwhYAAICkCVsAAACSJmwHqNra2viv//W/xhFHHBGjRo2KmTNnxs9+9rMec7Isi1tuuSXGjh0bw4YNi9/93d+N119/vcecrq6u+Pa3vx0jR46Mww8/PL7+9a/H5s2be8z5j//4j7j00kujtLQ0SktL49JLL433338/17t4QOTzOP35n/95VFZWxmGHHRZHHnlkrnftgMnXMfrFL34Rc+bMifLy8hg2bFgcd9xxcfPNN8e2bdvysp/7K5/n0te//vU4+uijo7i4OMaMGROXXnppvPPOOznfx/2Vz2P0m3NPOeWUKCgoiLVr1+Zq1w6ofB6nY445JgoKCno8brjhhpzv44GQ7/Pp//7f/xtTp06NYcOGxciRI+MP//APc7p/B0K+jtGPf/zjXufRrscrr7ySl33dH/k8l9544434xje+ESNHjoySkpI4/fTT47nnnsv5Ph4I+TxOP/nJT+Kcc86JI488MkaMGBH/43/8j/jggw9yvo/760Ado/vuuy9+93d/N0pKSqKgoKDP6+qUr78HrIwB6dxzz80eeOCB7N///d+ztWvXZueff3529NFHZx988EH3nL/4i7/IjjjiiOzxxx/PXnvttWzWrFnZmDFjss7Ozu451dXV2VFHHZU1NDRkP/nJT7IzzzwzO/nkk7Pt27d3z/m93/u9bNKkSdmqVauyVatWZZMmTcp+//d/P6/7u6/yeZxuuumm7Pbbb89qamqy0tLSfO7mfsnXMfqHf/iH7PLLL8+eeeaZ7M0338z+9m//Nhs1alT2ne98J+/7vC/yeS7dfvvt2erVq7Nf/OIX2UsvvZRNmzYtmzZtWl73d1/k8xjtMnfu3GzGjBlZRGRNTU352M39ls/jNGHChGzRokVZS0tL92Pr1q153d99lc/j9Nhjj2Wf//znsyVLlmQ/+9nPsp/+9KfZo48+mtf93Rf5OkZdXV09zqGWlpbsyiuvzI455phs586ded/vvZXPc+mLX/xidt5552Wvvvpq9sYbb2RXXXVVdthhh2UtLS153ed9ka/j9Pbbb2ef//zns+rq6uynP/1p9q//+q9ZZWVlduGFF+Z9n/fWgTpGf/VXf5XV1tZmtbW1WURk//Ef/9Hre6V8/T1QCdtEbNmyJYuI7Pnnn8+yLMt27tyZjR49OvuLv/iL7jm//vWvs9LS0uyee+7JsizL3n///WzIkCHZj370o+45b7/9djZo0KDs6aefzrIsy9atW5dFRPbyyy93z1m9enUWEdlPf/rTfOzaAZWr4/SbHnjggaTC9rfl4xjt8r3vfS8rLy/P0Z7kVj6P09/+7d9mBQUF2bZt23K0N7mR62NUX1+fnXjiidnrr7+eVNj+tlwepwkTJmR/9Vd/lZ8dybFcHaePP/44O+qoo7L7778/j3uTG/n6ubRt27Zs1KhR2aJFi3K4N7mTq+P0y1/+MouI7IUXXuie09nZmUVE9o//+I/52LUDKlfH6d57781GjRqV7dixo3tOU1NTFhHZz3/+83zs2gGzL8foNz333HN9hu3Bdv09UHgpciI6OjoiImL48OEREbFx48ZobW2Nqqqq7jlFRUVxxhlnxKpVqyIiorGxMT7++OMec8aOHRuTJk3qnrN69eooLS2NqVOnds857bTTorS0tHtOSnJ1nA4m+TxGHR0d3d8nNfk6Tu+9914sX748KisrY8iQIbnanZzI5TF6991347//9/8e/+f//J847LDD8rE7OZPrc+m73/1ujBgxIk455ZT48z//82Re/v/bcnWcfvKTn8Tbb78dgwYNit/5nd+JMWPGxIwZM3q9dDAF+fq5tGLFimhra4vLL788R3uSW7k6TiNGjIiJEyfGQw89FB9++GFs37497r333igrK4uKiop87d4Bk6vj1NXVFUOHDo1Bg/4zM4YNGxYRES+++GJud+oA25djtCcOtuvvgULYJiDLsqipqYmvfvWrMWnSpIiIaG1tjYiIsrKyHnPLysq6/6y1tTWGDh0an//85z91zqhRo3p9z1GjRnXPSUUuj9PBIp/H6M0334w777wzqqurD/Ru5Fw+jtP1118fhx9+eIwYMSKam5vjb//2b3O1OzmRy2OUZVlcfvnlUV1dHVOmTMn1ruRUrs+lP/3TP40f/ehH8dxzz8U111wTP/jBD+Kqq67K5S7lRC6P04YNGyIi4pZbbok/+7M/i7//+7+Pz3/+83HGGWfEe++9l9P9OpDy+fN76dKlce6558b48eMP9G7kXC6PU0FBQTQ0NERTU1McccQRUVxcHH/1V38VTz/9dFK/eyMit8fprLPOitbW1vjLv/zL2LZtW/zHf/xH3HjjjRER0dLSktP9OpD29RjtiYPp+nsgEbYJuOaaa+Lf/u3f4pFHHun1ZwUFBT2eZ1nWa+y3/facvubvydcZaHJ9nA4G+TpG77zzTvze7/1e/Lf/9t/iyiuv3L9F94N8HKfrrrsumpqa4tlnn43BgwfH7NmzI8uy/V98nuTyGN15553R2dkZCxYsOHAL7ie5Ppfmz58fZ5xxRpx00klx5ZVXxj333BNLly6N9vb2A7MDeZLL47Rz586IiFi4cGFceOGFUVFREQ888EAUFBTEo48+eoD2IPfy9fN78+bN8cwzz8ScOXP2b8H9JJfHKcuyuOqqq2LUqFGxcuXK+Nd//df4xje+Eb//+7+fVLBF5PY4feUrX4m//uu/ju9///tx2GGHxejRo+PYY4+NsrKyGDx48IHbiRw70Mfos77Gvn4d/pOwHeC+/e1vx4oVK+K5556LcePGdY+PHj06IqLXv+ps2bKl+1+RRo8e3f0vZZ8259133+31fX/5y1/2+teogSzXx+lgkK9j9M4778SZZ54Z06ZNi/vuuy8Xu5JT+TpOI0eOjBNOOCHOOeec+NGPfhT19fXx8ssv52KXDrhcH6N//ud/jpdffjmKioqisLAwvvjFL0ZExJQpU+Kyyy7L2X4daP3xc+m0006LiIj/9//+3wHZh3zI9XEaM2ZMRER8+ctf7v7zoqKiOPbYY6O5ufnA71AO5PNceuCBB2LEiBHx9a9//UDvRs7l42fT3//938ePfvSjOP3002Py5MmxePHiGDZsWPz1X/91LnftgMrH+XTJJZdEa2trvP3229He3h633HJL/PKXv4zy8vJc7dYBtT/HaE8cLNffA05O38HLPtu5c2d29dVXZ2PHjs3eeOONPv989OjR2Xe/+93usa6urj7f4F9XV9c955133unzl0f9y7/8S/ecl19+OZk3r+frOP2m1H55VD6P0ebNm7Pjjz8+u/jii/v8DbcDWX+cS7s0NzdnEZE999xzB26HciBfx+itt97KXnvtte7HM888k0VE9thjj2WbNm3K8V7uv/48l/7u7/4ui4jsrbfeOoB7lBv5Ok4dHR1ZUVFRj18eteuXI91777252r0DIt/n0s6dO7Py8vJkfpv9Lvk6TitWrMgGDRrU6zePn3DCCdmf//mf52LXDqj+/Nm0dOnS7LDDDuvztwMPJAfiGP2mz/rlUalefw9UwnaA+p//839mpaWl2Y9//OMev37/o48+6p7zF3/xF1lpaWn2xBNPZK+99lr2x3/8x33+SvZx48Zl//iP/5j95Cc/yc4666w+P+7npJNOylavXp2tXr06+y//5b8k8+vG83mc3nrrraypqSm79dZbs8997nNZU1NT1tTUNOA/WiNfx+jtt9/OvvjFL2ZnnXVWtnnz5h7fKwX5Ok7/8i//kt15551ZU1NT9otf/CL753/+5+yrX/1qdtxxx2W//vWv877feyOff99+08aNG5P6rcj5Ok6rVq3Kbr/99qypqSnbsGFDVldXl40dOzb7+te/nvd93hf5PJ/+9E//NDvqqKOyZ555JvvpT3+azZkzJxs1alT23nvv5XWf91a+/8794z/+YxYR2bp16/K2jwdCvo7TL3/5y2zEiBHZH/7hH2Zr167Nfvazn2XXXnttNmTIkGzt2rV53++9lc/z6c4778waGxuzn/3sZ9ldd92VDRs2LPvhD3+Y1/3dFwfqGLW0tGRNTU3Z//7f/7v7N2k3NTVl7e3t3XNSvv4eqITtABURfT4eeOCB7jk7d+7Mbr755mz06NFZUVFR9v/9f/9f9tprr/X4Or/61a+ya665Jhs+fHg2bNiw7Pd///ez5ubmHnPa29uzb37zm9kRRxyRHXHEEdk3v/nNAf8varvk8zhddtllfX6vgX6XLV/H6IEHHtjt90pBvo7Tv/3bv2VnnnlmNnz48KyoqCg75phjsurq6mzz5s352tV9ls+/b78ptbDN13FqbGzMpk6dmpWWlmbFxcXZl770pezmm2/OPvzww3zt6n7J5/m0bdu27Dvf+U42atSo7IgjjsjOPvvs7N///d/zsZv7Jd9/5/74j/84q6yszPVu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9tlnY8qUKTFkyJA+tykqKoqioqJe46WlpX5Ic0CUlJQ4lzggnEscSM4nDhTnEgfKoEF+jVF/cNTzoKamJu6///5YtmxZrF+/PubPnx/Nzc1RXV0dEZ/cbZ09e3b3/Orq6njrrbeipqYm1q9fH8uWLYulS5fGtdde21+7AAAAMGC5Y5sHs2bNivb29li0aFG0tLTEpEmTor6+PiZMmBARES0tLT0+07a8vDzq6+tj/vz5cffdd8fYsWPjjjvuiAsvvLC/dgEAAGDAErZ5ctVVV8VVV13V5589+OCDvcbOOOOM+MlPfrLP36+oqChuvvnmPl+eDHvDucSB4lziQHI+caA4lzhQnEv9qyDz+6gBAABImPfYAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2CZs8eLFUV5eHsXFxVFRURErV6781PnPP/98VFRURHFxcRx77LFxzz335GmlDHR7cy498cQTcc4558QXvvCFKCkpiWnTpsUzzzyTx9UykO3tz6VdXnrppSgsLIxTTjkltwskGXt7LnV1dcXChQtjwoQJUVRUFMcdd1wsW7YsT6tloNvb82n58uVx8sknx2GHHRZjxoyJK664Itrb2/O0WgaqF154IS644IIYO3ZsFBQUxFNPPfWZ27j+zh9hm6i6urqYN29eLFy4MJqammL69OkxY8aMHp+H+5s2btwY5513XkyfPj2amprixhtvjLlz58bjjz+e55Uz0OztufTCCy/EOeecE/X19dHY2BhnnnlmXHDBBdHU1JTnlTPQ7O25tEtHR0fMnj07vva1r+VppQx0+3IuXXTRRfFP//RPsXTp0vjZz34WjzzySJx44ol5XDUD1d6eTy+++GLMnj075syZE6+//no8+uij8corr8SVV16Z55Uz0Hz44Ydx8sknx1133bVH811/51lGkk499dSsurq6x9iJJ56Y3XDDDX3O/1//639lJ554Yo+xb33rW9lpp52WszWShr09l/ry5S9/Obv11lsP9NJIzL6eS7Nmzcr+7M/+LLv55puzk08+OYcrJBV7ey79wz/8Q1ZaWpq1t7fnY3kkZm/Pp7/8y7/Mjj322B5jd9xxRzZu3LicrZH0RET25JNPfuoc19/55Y5tgrZt2xaNjY1RVVXVY7yqqipWrVrV5zarV6/uNf/cc8+NNWvWxMcff5yztTKw7cu59Nt27twZ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atSo3c6rra2N0tLS7sf48eP3e80AAAApELY5NnLkyBg8eHCvu7NbtmzpdRf3t9XV1cWcOXPib/7mb+Lss8/+1LkLFiyIjo6O7semTZv2e+0AAAApELY5NnTo0KioqIiGhoYe4w0NDVFZWbnb7R555JG4/PLL4+GHH47zzz//M79PUVFRlJSU9HgAAAAcCrzHNg9qamri0ksvjSlTpsS0adPivvvui+bm5qiuro6IT+62vv322/HQQw9FxCdRO3v27PjhD38Yp512Wvfd3mHDhkVpaWm/7QcAAMBAJGzzYNasWdHe3h6LFi2KlpaWmDRpUtTX18eECRMiIqKlpaXHZ9ree++9sX379rj66qvj6quv7h6/7LLL4sEHH8z38gEAAAY0n2N7kPI5WgAAkD+uv/uX99gCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2ObJ4sWLo7y8PIqLi6OioiJWrlz5qfOff/75qKioiOLi4jj22GPjnnvuydNKAQAA0iJs86Curi7mzZsXCxcujKamppg+fXrMmDEjmpub+5y/cePGOO+882L69OnR1NQUN954Y8ydOzcef/zxPK8cAABg4CvIsizr70Uc7KZOnRqTJ0+OJUuWdI9NnDgxZs6cGbW1tb3mX3/99bFixYpYv35991h1dXW8+uqrsXr16j36np2dnVFaWhodHR1RUlKy/zsBAADsluvv/lXY3ws42G3bti0aGxvjhhtu6DFeVVUVq1at6nOb1atXR1VVVY+xc889N5YuXRoff/xxDBkypNc2XV1d0dXV1f28o6MjIj75CwYAAOTWrutu9w37h7DNsba2ttixY0eUlZX1GC8rK4vW1tY+t2ltbe1z/vbt26OtrS3GjBnTa5va2tq49dZbe42PHz9+P1YPAADsjfb29igtLe3vZRxyhG2eFBQU9HieZVmvsc+a39f4LgsWLIiampru5++//35MmDAhmpub/cViv3R2dsb48eNj06ZNXlbDfnEucSA5nzhQnEscKB0dHXH00UfH8OHD+3sphyRhm2MjR46MwYMH97o7u2XLll53ZXcZPXp0n/MLCwtjxIgRfW5TVFQURUVFvcZLS0v9kOaAKCkpcS5xQDiXOJCcTxwoziUOlEGD/H7e/uCo59jQoUOjoqIiGhoaeow3NDREZWVln9tMmzat1/xnn302pkyZ0uf7awEAAA5lwjYPampq4v77749ly5bF+vXrY/78+dHc3BzV1dUR8cnLiGfPnt09v7q6Ot56662oqamJ9evXx7Jly2Lp0qVx7bXX9tcuAAAADFheipwHs2bNivb29li0aFG0tLTEpEmTor6+PiZMmBARES0tLT0+07a8vDzq6+tj/vz5cffdd8fYsWPjjjvuiAsvvHCPv2dRUVHcfPPNfb48GfaGc4kDxbnEgeR84kBxLnGgOJf6l8+xBQAAIGleigwAAEDShC0AAABJE7YAAAAkTdgCAACQNGGbsMWLF0d5eXkUFxdHRUVFrFy58lPnP//881FRURHFxcVx7LHHxj333JOnlTLQ7c259MQTT8Q555wTX/jCF6KkpCSmTZsWzzzzTB5Xy0C2tz+XdnnppZeisLAwTjnllNwukGTs7bnU1dUVCxcujAkTJkRRUVEcd9xxsWzZsjytloFub8+n5cuXx8knnxyHHXZYjBkzJq644opob2/P02oZqF544YW44IILYuzYsVFQUBBPPfXUZ27j+jt/hG2i6urqYt68ebFw4cJoamqK6dOnx4wZM3p8bNBv2rhxY5x33nkxffr0aGpqihtvvDHmzp0bjz/+eJ5XzkCzt+fSCy+8EOecc07U19dHY2NjnHnmmXHBBRdEU1NTnlfOQLO359IuHR0dMXv27Pja176Wp5Uy0O3LuXTRRRfFP/3TP8XSpUvjZz/7WTzyyCNx4okn5nHVDFR7ez69+OKLMXv27JgzZ068/vrr8eijj8Yrr7wSV155ZZ5XzkDz4Ycfxsknnxx33XXXHs13/Z1nGUk69dRTs+rq6h5jJ554YnbDDTf0Of9//a//lZ144ok9xr71rW9lp512Ws7WSBr29lzqy5e//OXs1ltvPdBLIzH7ei7NmjUr+7M/+7Ps5ptvzk4++eQcrpBU7O259A//8A9ZaWlp1t7eno/lkZi9PZ/+8i//Mjv22GN7jN1xxx3ZuHHjcrZG0hMR2ZNPPvmpc1x/55c7tgnatm1bNDY2RlVVVY/xqqqqWLVqVZ/brF69utf8c889N9asWRMff/xxztbKwLYv59Jv27lzZ2zdujWGDx+eiyWSiH09lx544IF488034+abb871EknEvpxLK1asiClTpsT3vve9OOqoo+KEE06Ia6+9Nn71q1/lY8kMYPtyPlVWVsbmzZujvr4+siyLd999Nx577LE4//zz87FkDiKuv/OrsL8XwN5ra2uLHTt2RFlZWY/xsrKyaG1t7XOb1tbWPudv37492traYsyYMTlbLwPXvpxLv+373/9+fPjhh3HRRRflYokkYl/OpZ///Odxww03xMqVK6Ow0P+O+MS+nEsbNmyIF198MYqLi+PJJ5+Mtra2uOqqq+K9997zPttD3L6cT5WVlbF8+fKYNWtW/PrXv47t27fH17/+9bjzzjvzsWQOIq6/88sd24QVFBT0eJ5lWa+xz5rf1ziHnr09l3Z55JFH4pZbbom6uroYNWpUrpZHQvb0XNqxY0dccsklceutt8YJJ5yQr+WRkL35ubRz584oKCiI5cuXx6mnnhrnnXde3H777fHggw+6a0tE7N35tG7dupg7d27cdNNN0djYGE8//XRs3Lgxqqur87FUDjKuv/PHP5EnaOTIkTF48OBe/9K4ZcuWXv8qtMvo0aP7nF9YWBgjRozI2VoZ2PblXNqlrq4u5syZE48++micffbZuVwmCdjbc2nr1q2xZs2aaGpqimuuuSYiPomTLMuisLAwnn322TjrrLPysnYGln35uTRmzJg46qijorS0tHts4sSJkWVZbN68OY4//vicrpmBa1/Op9ra2jj99NPjuuuui4iIk046KQ4//PCYPn163Hbbbe6yscdcf+eXO7YJGjp0aFRUVERDQ0OP8YaGhqisrOxzm2nTpvWa/+yzz8aUKVNiyJAhOVsrA9u+nEsRn9ypvfzyy+Phhx/2niMiYu/PpZKSknjttddi7dq13Y/q6ur40pe+FGvXro2pU6fma+kMMPvyc+n000+Pd955Jz744IPusTfeeCMGDRoU48aNy+l6Gdj25Xz66KOPYtCgnpfIgwcPjoj/vNsGe8L1d5710y+tYj/96Ec/yoYMGZItXbo0W7duXTZv3rzs8MMPz37xi19kWZZlN9xwQ3bppZd2z9+wYUN22GGHZfPnz8/WrVuXLV26NBsyZEj22GOP9dcuMEDs7bn08MMPZ4WFhdndd9+dtbS0dD/ef//9/toFBoi9PZd+m9+KzC57ey5t3bo1GzduXPZHf/RH2euvv549//zz2fHHH59deeWV/bULDCB7ez498MADWWFhYbZ48eLszTffzF588cVsypQp2amnntpfu8AAsXXr1qypqSlramrKIiK7/fbbs6ampuytt97Kssz1d38Ttgm7++67swkTJmRDhw7NJk+enD3//PPdf3bZZZdlZ5xxRo/5P/7xj7Pf+Z3fyYYOHZodc8wx2ZIlS/K8YgaqvTmXzjjjjCwiej0uu+yy/C+cAWdvfy79JmHLb9rbc2n9+vXZ2WefnQ0bNiwbN25cVlNTk3300Ud5XjUD1d6eT3fccUf25S9/ORs2bFg2ZsyY7Jvf/Ga2efPmPK+agea555771Gsg19/9qyDLvKYCAACAdHmPLQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACTt/wcLAF7/XlacegAAAABJRU5ErkJggg==", + "text/html": [ + "\n", + "
\n", + "
\n", + " Figure\n", + "
\n", + " \n", + "
\n", + " " + ], + "text/plain": [ + "Canvas(toolbar=Toolbar(toolitems=[('Home', 'Reset original view', 'home', 'home'), ('Back', 'Back to previous …" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'cloudfraction'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'cloudfraction'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/SFCCLDGRID/.ipynb_checkpoints/sfccldgrid2longcaracena.c1-checkpoint.ipynb b/VAPs/quicklook/SFCCLDGRID/.ipynb_checkpoints/sfccldgrid2longcaracena.c1-checkpoint.ipynb new file mode 100644 index 00000000..d7d3c877 --- /dev/null +++ b/VAPs/quicklook/SFCCLDGRID/.ipynb_checkpoints/sfccldgrid2longcaracena.c1-checkpoint.ipynb @@ -0,0 +1,5150 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# SFCCLDGRID2LONGCARACENA.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/sfccldgrid) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'sfccldgrid2longcaracena'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2020-06-01', 'facility': 'N1', 'site': 'sgp', 'start_date': '2011-10-21'}]" + ] + }, + { + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "ac6764f5", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The following locations and date ranges are available for this VAP:\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
sitefacilitystart_dateend_date
0sgpN12011-10-212020-06-01
\n", + "
" + ], + "text/plain": [ + " site facility start_date end_date\n", + "0 sgp N1 2011-10-21 2020-06-01" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'N1' )\n", + "\n", + "date_start = '2020-05-29'\n", + "date_end = '2020-05-31'" + ] + }, + { + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'/data/archive/sgp/sgpsfccldgrid2longcaracenaN1.c1'" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['20200529', '20200530', '20200531']" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "51feea2e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['/data/archive/sgp/sgpsfccldgrid2longcaracenaN1.c1/sgpsfccldgrid2longcaracenaN1.c1.20200529.060000.nc',\n", + " '/data/archive/sgp/sgpsfccldgrid2longcaracenaN1.c1/sgpsfccldgrid2longcaracenaN1.c1.20200530.060000.nc',\n", + " '/data/archive/sgp/sgpsfccldgrid2longcaracenaN1.c1/sgpsfccldgrid2longcaracenaN1.c1.20200531.060000.nc']" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "3 files loaded\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
<xarray.Dataset>\n",
+       "Dimensions:                                                         (time: 288,\n",
+       "                                                                     bound: 2,\n",
+       "                                                                     lat: 8,\n",
+       "                                                                     lon: 11)\n",
+       "Coordinates:\n",
+       "  * time                                                            (time) datetime64[ns] ...\n",
+       "  * lat                                                             (lat) float32 ...\n",
+       "  * lon                                                             (lon) float32 ...\n",
+       "Dimensions without coordinates: bound\n",
+       "Data variables: (12/59)\n",
+       "    base_time                                                       (time) datetime64[ns] ...\n",
+       "    time_offset                                                     (time) datetime64[ns] ...\n",
+       "    time_bounds                                                     (time, bound) object dask.array<chunksize=(96, 2), meta=np.ndarray>\n",
+       "    downwelling_shortwave                                           (time, lat, lon) float32 dask.array<chunksize=(96, 8, 11), meta=np.ndarray>\n",
+       "    source_central_facility_downwelling_shortwave                   (time) int32 dask.array<chunksize=(96,), meta=np.ndarray>\n",
+       "    qc_downwelling_shortwave                                        (time, lat, lon) int32 dask.array<chunksize=(96, 8, 11), meta=np.ndarray>\n",
+       "    ...                                                              ...\n",
+       "    qc_visible_cloud_optical_depth                                  (time, lat, lon) int32 dask.array<chunksize=(96, 8, 11), meta=np.ndarray>\n",
+       "    cloud_radiating_temperature                                     (time, lat, lon) float32 dask.array<chunksize=(96, 8, 11), meta=np.ndarray>\n",
+       "    source_central_facility_cloud_radiating_temperature             (time) int32 dask.array<chunksize=(96,), meta=np.ndarray>\n",
+       "    qc_cloud_radiating_temperature                                  (time, lat, lon) int32 dask.array<chunksize=(96, 8, 11), meta=np.ndarray>\n",
+       "    azimuth                                                         (time) float32 dask.array<chunksize=(96,), meta=np.ndarray>\n",
+       "    alt                                                             (time, lat, lon) float32 dask.array<chunksize=(96, 8, 11), meta=np.ndarray>\n",
+       "Attributes: (12/17)\n",
+       "    command_line:          sfccldgrid2long_caracena -s sgp -f N1 -b 20171001 ...\n",
+       "    Conventions:           ARM-1.3\n",
+       "    process_version:       vap-sfccldgrid2long_caracena-1.4-0.el7\n",
+       "    dod_version:           sfccldgrid2longcaracena-c1-1.2\n",
+       "    input_datastreams:     sgpsfccldgrid2longstationN1.c1 : 1.4 : 20200529.06...\n",
+       "    site_id:               sgp\n",
+       "    ...                    ...\n",
+       "    doi:                   10.5439/1393588\n",
+       "    history:               created by user gaustad on machine agate at 2022-0...\n",
+       "    _file_dates:           ['20200529', '20200530', '20200531']\n",
+       "    _file_times:           ['060000', '060000', '060000']\n",
+       "    _datastream:           sgpsfccldgrid2longcaracenaN1.c1\n",
+       "    _arm_standards_flag:   1
" + ], + "text/plain": [ + "\n", + "Dimensions: (time: 288,\n", + " bound: 2,\n", + " lat: 8,\n", + " lon: 11)\n", + "Coordinates:\n", + " * time (time) datetime64[ns] ...\n", + " * lat (lat) float32 ...\n", + " * lon (lon) float32 ...\n", + "Dimensions without coordinates: bound\n", + "Data variables: (12/59)\n", + " base_time (time) datetime64[ns] ...\n", + " time_offset (time) datetime64[ns] ...\n", + " time_bounds (time, bound) object dask.array\n", + " downwelling_shortwave (time, lat, lon) float32 dask.array\n", + " source_central_facility_downwelling_shortwave (time) int32 dask.array\n", + " qc_downwelling_shortwave (time, lat, lon) int32 dask.array\n", + " ... ...\n", + " qc_visible_cloud_optical_depth (time, lat, lon) int32 dask.array\n", + " cloud_radiating_temperature (time, lat, lon) float32 dask.array\n", + " source_central_facility_cloud_radiating_temperature (time) int32 dask.array\n", + " qc_cloud_radiating_temperature (time, lat, lon) int32 dask.array\n", + " azimuth (time) float32 dask.array\n", + " alt (time, lat, lon) float32 dask.array\n", + "Attributes: (12/17)\n", + " command_line: sfccldgrid2long_caracena -s sgp -f N1 -b 20171001 ...\n", + " Conventions: ARM-1.3\n", + " process_version: vap-sfccldgrid2long_caracena-1.4-0.el7\n", + " dod_version: sfccldgrid2longcaracena-c1-1.2\n", + " input_datastreams: sgpsfccldgrid2longstationN1.c1 : 1.4 : 20200529.06...\n", + " site_id: sgp\n", + " ... ...\n", + " doi: 10.5439/1393588\n", + " history: created by user gaustad on machine agate at 2022-0...\n", + " _file_dates: ['20200529', '20200530', '20200531']\n", + " _file_times: ['060000', '060000', '060000']\n", + " _datastream: sgpsfccldgrid2longcaracenaN1.c1\n", + " _arm_standards_flag: 1" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['downwelling_shortwave', 'source_central_facility_downwelling_shortwave', 'clearsky_downwelling_shortwave']" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [ + { + "ename": "TypeError", + "evalue": "Dimensions of C (11, 8, 288) should be one smaller than X(288) and Y(8) while using shading='flat' see help(pcolormesh)", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[0;32mIn[9], line 5\u001b[0m\n\u001b[1;32m 2\u001b[0m ts_display\u001b[38;5;241m.\u001b[39madd_subplots((\u001b[38;5;28mlen\u001b[39m(variables_to_plot),), figsize \u001b[38;5;241m=\u001b[39m (\u001b[38;5;241m9.5\u001b[39m,\u001b[38;5;241m4\u001b[39m\u001b[38;5;241m*\u001b[39m\u001b[38;5;28mlen\u001b[39m(variables_to_plot)))\n\u001b[1;32m 4\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m i,v \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28menumerate\u001b[39m(variables_to_plot):\n\u001b[0;32m----> 5\u001b[0m ts_ax \u001b[38;5;241m=\u001b[39m \u001b[43mts_display\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mplot\u001b[49m\u001b[43m(\u001b[49m\u001b[43mv\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43msubplot_index\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mi\u001b[49m\u001b[43m,\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mset_title\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mds\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mvariables\u001b[49m\u001b[43m[\u001b[49m\u001b[43mv\u001b[49m\u001b[43m]\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mattrs\u001b[49m\u001b[43m[\u001b[49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43mlong_name\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m,\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 6\u001b[0m ts_ax\u001b[38;5;241m.\u001b[39mgrid()\n\u001b[1;32m 8\u001b[0m plt\u001b[38;5;241m.\u001b[39mshow()\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/act/plotting/timeseriesdisplay.py:588\u001b[0m, in \u001b[0;36mTimeSeriesDisplay.plot\u001b[0;34m(self, field, dsname, subplot_index, cmap, set_title, add_nan, day_night_background, invert_y_axis, abs_limits, time_rng, y_rng, use_var_for_y, set_shading, assessment_overplot, overplot_marker, overplot_behind, overplot_markersize, assessment_overplot_category, assessment_overplot_category_color, force_line_plot, labels, cbar_label, cbar_h_adjust, secondary_y, y_axis_flag_meanings, colorbar_labels, cb_friendly, **kwargs)\u001b[0m\n\u001b[1;32m 586\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124medgecolors\u001b[39m\u001b[38;5;124m'\u001b[39m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;129;01min\u001b[39;00m kwargs\u001b[38;5;241m.\u001b[39mkeys():\n\u001b[1;32m 587\u001b[0m kwargs[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124medgecolors\u001b[39m\u001b[38;5;124m'\u001b[39m] \u001b[38;5;241m=\u001b[39m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mface\u001b[39m\u001b[38;5;124m'\u001b[39m\n\u001b[0;32m--> 588\u001b[0m mesh \u001b[38;5;241m=\u001b[39m \u001b[43max\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mpcolormesh\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 589\u001b[0m \u001b[43m \u001b[49m\u001b[43mnp\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43masarray\u001b[49m\u001b[43m(\u001b[49m\u001b[43mxdata\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 590\u001b[0m \u001b[43m \u001b[49m\u001b[43mydata\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 591\u001b[0m \u001b[43m \u001b[49m\u001b[43mdata\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mtranspose\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 592\u001b[0m \u001b[43m \u001b[49m\u001b[43mshading\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mset_shading\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 593\u001b[0m \u001b[43m \u001b[49m\u001b[43mcmap\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcmap\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 594\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 595\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 597\u001b[0m \u001b[38;5;66;03m# Set Title\u001b[39;00m\n\u001b[1;32m 598\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m set_title \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/matplotlib/__init__.py:1442\u001b[0m, in \u001b[0;36m_preprocess_data..inner\u001b[0;34m(ax, data, *args, **kwargs)\u001b[0m\n\u001b[1;32m 1439\u001b[0m \u001b[38;5;129m@functools\u001b[39m\u001b[38;5;241m.\u001b[39mwraps(func)\n\u001b[1;32m 1440\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21minner\u001b[39m(ax, \u001b[38;5;241m*\u001b[39margs, data\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mNone\u001b[39;00m, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs):\n\u001b[1;32m 1441\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m data \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[0;32m-> 1442\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mfunc\u001b[49m\u001b[43m(\u001b[49m\u001b[43max\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;28;43mmap\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43msanitize_sequence\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43margs\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 1444\u001b[0m bound \u001b[38;5;241m=\u001b[39m new_sig\u001b[38;5;241m.\u001b[39mbind(ax, \u001b[38;5;241m*\u001b[39margs, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs)\n\u001b[1;32m 1445\u001b[0m auto_label \u001b[38;5;241m=\u001b[39m (bound\u001b[38;5;241m.\u001b[39marguments\u001b[38;5;241m.\u001b[39mget(label_namer)\n\u001b[1;32m 1446\u001b[0m \u001b[38;5;129;01mor\u001b[39;00m bound\u001b[38;5;241m.\u001b[39mkwargs\u001b[38;5;241m.\u001b[39mget(label_namer))\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/matplotlib/axes/_axes.py:6220\u001b[0m, in \u001b[0;36mAxes.pcolormesh\u001b[0;34m(self, alpha, norm, cmap, vmin, vmax, shading, antialiased, *args, **kwargs)\u001b[0m\n\u001b[1;32m 6217\u001b[0m shading \u001b[38;5;241m=\u001b[39m shading\u001b[38;5;241m.\u001b[39mlower()\n\u001b[1;32m 6218\u001b[0m kwargs\u001b[38;5;241m.\u001b[39msetdefault(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124medgecolors\u001b[39m\u001b[38;5;124m'\u001b[39m, \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mnone\u001b[39m\u001b[38;5;124m'\u001b[39m)\n\u001b[0;32m-> 6220\u001b[0m X, Y, C, shading \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_pcolorargs\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43mpcolormesh\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 6221\u001b[0m \u001b[43m \u001b[49m\u001b[43mshading\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mshading\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mkwargs\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 6222\u001b[0m coords \u001b[38;5;241m=\u001b[39m np\u001b[38;5;241m.\u001b[39mstack([X, Y], axis\u001b[38;5;241m=\u001b[39m\u001b[38;5;241m-\u001b[39m\u001b[38;5;241m1\u001b[39m)\n\u001b[1;32m 6223\u001b[0m \u001b[38;5;66;03m# convert to one dimensional array, except for 3D RGB(A) arrays\u001b[39;00m\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/matplotlib/axes/_axes.py:5751\u001b[0m, in \u001b[0;36mAxes._pcolorargs\u001b[0;34m(self, funcname, shading, *args, **kwargs)\u001b[0m\n\u001b[1;32m 5749\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m shading \u001b[38;5;241m==\u001b[39m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mflat\u001b[39m\u001b[38;5;124m'\u001b[39m:\n\u001b[1;32m 5750\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m (Nx, Ny) \u001b[38;5;241m!=\u001b[39m (ncols \u001b[38;5;241m+\u001b[39m \u001b[38;5;241m1\u001b[39m, nrows \u001b[38;5;241m+\u001b[39m \u001b[38;5;241m1\u001b[39m):\n\u001b[0;32m-> 5751\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mTypeError\u001b[39;00m(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mDimensions of C \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mC\u001b[38;5;241m.\u001b[39mshape\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m should\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 5752\u001b[0m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m be one smaller than X(\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mNx\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m) and Y(\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mNy\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m)\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 5753\u001b[0m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m while using shading=\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mflat\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 5754\u001b[0m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m see help(\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mfuncname\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m)\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[1;32m 5755\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m: \u001b[38;5;66;03m# ['nearest', 'gouraud']:\u001b[39;00m\n\u001b[1;32m 5756\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m (Nx, Ny) \u001b[38;5;241m!=\u001b[39m (ncols, nrows):\n", + "\u001b[0;31mTypeError\u001b[0m: Dimensions of C (11, 8, 288) should be one smaller than X(288) and Y(8) while using shading='flat' see help(pcolormesh)" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "dd49c62706534d8988ce7d6a5c25f646", + "version_major": 2, + "version_minor": 0 + }, + "image/png": "iVBORw0KGgoAAAANSUhEUgAAA7YAAASwCAYAAADPBNYLAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAA9hAAAPYQGoP6dpAABi0UlEQVR4nO3df3RV5Z3o/08gkKA1sUAJIIjRaqXlqkO4IrF8Ha3GQceWGeeKY5eog/c2Vy0DqV5FZvmD5axMO6tO6w9Qr6DjXWgz/hxmbkbNzFhFwRlJg+MIrb1CDWgiTRwT1DYI7O8fLjJNE5Rf5yQPvF5rnT/Ow7OTZ283cb/Z5+QUZFmWBQAAACRqUH8vAAAAAPaHsAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAnbHHvhhRfiggsuiLFjx0ZBQUE89dRTn7nN888/HxUVFVFcXBzHHnts3HPPPblfKAAAQKKEbY59+OGHcfLJJ8ddd921R/M3btwY5513XkyfPj2amprixhtvjLlz58bjjz+e45UCAACkqSDLsqy/F3GoKCgoiCeffDJmzpy52znXX399rFixItavX989Vl1dHa+++mqsXr06D6sEAABIS2F/L4CeVq9eHVVVVT3Gzj333Fi6dGl8/PHHMWTIkD636+rqiq6uru7nO3fujPfeey9GjBgRBQUFOV0zAAAc6rIsi61bt8bYsWNj0CAvjM03YTvAtLa2RllZWY+xsrKy2L59e7S1tcWYMWP63K62tjZuvfXWfCwRAADYjU2bNsW4ceP6exmHHGE7AP32HdZdrxb/tDuvCxYsiJqamu7nHR0dcfTRR8emTZuipKQkNwsFAAAiIqKzszPGjx8fRxxxRH8v5ZAkbAeY0aNHR2tra4+xLVu2RGFhYYwYMWK32xUVFUVRUVGv8ZKSEmELAAB54m2A/cOLvweYadOmRUNDQ4+xZ599NqZMmbLb99cCAAAcyoRtjn3wwQexdu3aWLt2bUR88nE+a9eujebm5oj45CXEs2fP7p5fXV0db731VtTU1MT69etj2bJlsXTp0rj22mv7Y/kAAAADnpci59iaNWvizDPP7H6+632wl112WTz44IPR0tLSHbkREeXl5VFfXx/z58+Pu+++O8aOHRt33HFHXHjhhXlfOwAAQAp8ju1BqrOzM0pLS6Ojo8N7bAEAIMdcf/cvL0UGAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbPNk8eLFUV5eHsXFxVFRURErV6781PnLly+Pk08+OQ477LAYM2ZMXHHFFdHe3p6n1QIAAKRD2OZBXV1dzJs3LxYuXBhNTU0xffr0mDFjRjQ3N/c5/8UXX4zZs2fHnDlz4vXXX49HH300XnnllbjyyivzvHIAAICBT9jmwe233x5z5syJK6+8MiZOnBg/+MEPYvz48bFkyZI+57/88stxzDHHxNy5c6O8vDy++tWvxre+9a1Ys2ZNnlcOAAAw8AnbHNu2bVs0NjZGVVVVj/GqqqpYtWpVn9tUVlbG5s2bo76+PrIsi3fffTcee+yxOP/883f7fbq6uqKzs7PHAwAA4FAgbHOsra0tduzYEWVlZT3Gy8rKorW1tc9tKisrY/ny5TFr1qwYOnRojB49Oo488si48847d/t9amtro7S0tPsxfvz4A7ofAAAAA5WwzZOCgoIez7Ms6zW2y7p162Lu3Llx0003RWNjYzz99NOxcePGqK6u3u3XX7BgQXR0dHQ/Nm3adEDXDwAAMFAV9vcCDnYjR46MwYMH97o7u2XLll53cXepra2N008/Pa677rqIiDjppJPi8MMPj+nTp8dtt90WY8aM6bVNUVFRFBUVHfgdAAAAGODcsc2xoUOHRkVFRTQ0NPQYb2hoiMrKyj63+eijj2LQoJ7/aQYPHhwRn9zpBQAA4D8J2zyoqamJ+++/P5YtWxbr16+P+fPnR3Nzc/dLixcsWBCzZ8/unn/BBRfEE088EUuWLIkNGzbESy+9FHPnzo1TTz01xo4d21+7AQAAMCB5KXIezJo1K9rb22PRokXR0tISkyZNivr6+pgwYUJERLS0tPT4TNvLL788tm7dGnfddVd85zvfiSOPPDLOOuus+O53v9tfuwAAADBgFWRe23pQ6uzsjNLS0ujo6IiSkpL+Xg4AABzUXH/3Ly9FBgAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmzzZPHixVFeXh7FxcVRUVERK1eu/NT5XV1dsXDhwpgwYUIUFRXFcccdF8uWLcvTagEAANJR2N8LOBTU1dXFvHnzYvHixXH66afHvffeGzNmzIh169bF0Ucf3ec2F110Ubz77ruxdOnS+OIXvxhbtmyJ7du353nlAAAAA19BlmVZfy/iYDd16tSYPHlyLFmypHts4sSJMXPmzKitre01/+mnn46LL744NmzYEMOHD9+n79nZ2RmlpaXR0dERJSUl+7x2AADgs7n+7l9eipxj27Zti8bGxqiqquoxXlVVFatWrepzmxUrVsSUKVPie9/7Xhx11FFxwgknxLXXXhu/+tWv8rFkAACApHgpco61tbXFjh07oqysrMd4WVlZtLa29rnNhg0b4sUXX4zi4uJ48skno62tLa666qp47733dvs+266urujq6up+3tnZeeB2AgAAYABzxzZPCgoKejzPsqzX2C47d+6MgoKCWL58eZx66qlx3nnnxe233x4PPvjgbu/a1tbWRmlpafdj/PjxB3wfAAAABiJhm2MjR46MwYMH97o7u2XLll53cXcZM2ZMHHXUUVFaWto9NnHixMiyLDZv3tznNgsWLIiOjo7ux6ZNmw7cTgAAAAxgwjbHhg4dGhUVFdHQ0NBjvKGhISorK/vc5vTTT4933nknPvjgg+6xN954IwYNGhTjxo3rc5uioqIoKSnp8QAAADgUCNs8qKmpifvvvz+WLVsW69evj/nz50dzc3NUV1dHxCd3W2fPnt09/5JLLokRI0bEFVdcEevWrYsXXnghrrvuuviTP/mTGDZsWH/tBgAAwIDkl0fl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pEmTor6+PiZMmBARES0tLT0+0/bee++N7du3x9VXXx1XX3119/hll10WDz74YL6XDwAAMKD5HNuDlM/RAgCA/HH93b+8xxYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwzZPFixdHeXl5FBcXR0VFRaxcufJT5z///PNRUVERxcXFceyxx8Y999yTp5UCAACkRdjmQV1dXcybNy8WLlwYTU1NMX369JgxY0Y0Nzf3OX/jxo1x3nnnxfTp06OpqSluvPHGmDt3bjz++ON5XjkAAMDAV5BlWdbfizjYTZ06NSZPnhxLlizpHps4cWLMnDkzamtre82//vrrY8WKFbF+/fruserq6nj11Vdj9erVe/Q9Ozs7o7S0NDo6OqKkpGT/dwIAANgt19/9yx3bHNu2bVs0NjZGVVVVj/GqqqpYtWpVn9usXr261/xzzz031qxZEx9//HHO1goAAJCiwv5ewMGura0tduzYEWVlZT3Gy8rKorW1tc9tWltb+5y/ffv2aGtrizFjxvTapqurK7q6urqfd3R0RMQn/3IEAADk1q7rbi+I7R/CNk8KCgp6PM+yrNfYZ83va3yX2trauPXWW3uNjx8/fm+XCgAA7KP29vYoLS3t72UccoRtjo0cOTIGDx7c6+7sli1bet2V3WX06NF9zi8sLIwRI0b0uc2CBQuipqam+/n7778fEyZMiObmZn+xPkVnZ2eMHz8+Nm3a5L0Qu+EY7RnH6bM5RnvGcdozjtNnc4z2jOP02RyjPdPR0RFHH310DB8+vL+XckgStjk2dOjQqKioiIaGhviDP/iD7vGGhob4xje+0ec206ZNi7/7u7/rMfbss8/GlClTYsiQIX1uU1RUFEVFRb3GS0tL/QDaAyUlJY7TZ3CM9ozj9Nkcoz3jOO0Zx+mzOUZ7xnH6bI7Rnhk0yK8x6g+Oeh7U1NTE/fffH8uWLYv169fH/Pnzo7m5OaqrqyPik7uts2fP7p5fXV0db731VtTU1MT69etj2bJlsXTp0rj22mv7axcAAAAGLHds82DWrFnR3t4eixYtipaWlpg0aVLU19fHhAkTIiKipaWlx2falpeXR319fcyfPz/uvvvuGDt2bNxxxx1x4YUX9tcuAAAADFjCNk+uuuqquOqqq/r8swcffLDX2BlnnBE/+clP9vn7FRUVxc0339zny5P5T47TZ3OM9ozj9Nkcoz3jOO0Zx+mzOUZ7xnH6bI7RnnGc+ldB5vdRAwAAkDDvsQUAACBpwhYAAICkCVsAAACSJmwHqNra2viv//W/xhFHHBGjRo2KmTNnxs9+9rMec7Isi1tuuSXGjh0bw4YNi9/93d+N119/vcecrq6u+Pa3vx0jR46Mww8/PL7+9a/H5s2be8z5j//4j7j00kujtLQ0SktL49JLL433338/17t4QOTzOP35n/95VFZWxmGHHRZHHnlkrnftgMnXMfrFL34Rc+bMifLy8hg2bFgcd9xxcfPNN8e2bdvysp/7K5/n0te//vU4+uijo7i4OMaMGROXXnppvPPOOznfx/2Vz2P0m3NPOeWUKCgoiLVr1+Zq1w6ofB6nY445JgoKCno8brjhhpzv44GQ7/Pp//7f/xtTp06NYcOGxciRI+MP//APc7p/B0K+jtGPf/zjXufRrscrr7ySl33dH/k8l9544434xje+ESNHjoySkpI4/fTT47nnnsv5Ph4I+TxOP/nJT+Kcc86JI488MkaMGBH/43/8j/jggw9yvo/760Ado/vuuy9+93d/N0pKSqKgoKDP6+qUr78HrIwB6dxzz80eeOCB7N///d+ztWvXZueff3529NFHZx988EH3nL/4i7/IjjjiiOzxxx/PXnvttWzWrFnZmDFjss7Ozu451dXV2VFHHZU1NDRkP/nJT7IzzzwzO/nkk7Pt27d3z/m93/u9bNKkSdmqVauyVatWZZMmTcp+//d/P6/7u6/yeZxuuumm7Pbbb89qamqy0tLSfO7mfsnXMfqHf/iH7PLLL8+eeeaZ7M0338z+9m//Nhs1alT2ne98J+/7vC/yeS7dfvvt2erVq7Nf/OIX2UsvvZRNmzYtmzZtWl73d1/k8xjtMnfu3GzGjBlZRGRNTU352M39ls/jNGHChGzRokVZS0tL92Pr1q153d99lc/j9Nhjj2Wf//znsyVLlmQ/+9nPsp/+9KfZo48+mtf93Rf5OkZdXV09zqGWlpbsyiuvzI455phs586ded/vvZXPc+mLX/xidt5552Wvvvpq9sYbb2RXXXVVdthhh2UtLS153ed9ka/j9Pbbb2ef//zns+rq6uynP/1p9q//+q9ZZWVlduGFF+Z9n/fWgTpGf/VXf5XV1tZmtbW1WURk//Ef/9Hre6V8/T1QCdtEbNmyJYuI7Pnnn8+yLMt27tyZjR49OvuLv/iL7jm//vWvs9LS0uyee+7JsizL3n///WzIkCHZj370o+45b7/9djZo0KDs6aefzrIsy9atW5dFRPbyyy93z1m9enUWEdlPf/rTfOzaAZWr4/SbHnjggaTC9rfl4xjt8r3vfS8rLy/P0Z7kVj6P09/+7d9mBQUF2bZt23K0N7mR62NUX1+fnXjiidnrr7+eVNj+tlwepwkTJmR/9Vd/lZ8dybFcHaePP/44O+qoo7L7778/j3uTG/n6ubRt27Zs1KhR2aJFi3K4N7mTq+P0y1/+MouI7IUXXuie09nZmUVE9o//+I/52LUDKlfH6d57781GjRqV7dixo3tOU1NTFhHZz3/+83zs2gGzL8foNz333HN9hu3Bdv09UHgpciI6OjoiImL48OEREbFx48ZobW2Nqqqq7jlFRUVxxhlnxKpVqyIiorGxMT7++OMec8aOHRuTJk3qnrN69eooLS2NqVOnds857bTTorS0tHtOSnJ1nA4m+TxGHR0d3d8nNfk6Tu+9914sX748KisrY8iQIbnanZzI5TF6991347//9/8e/+f//J847LDD8rE7OZPrc+m73/1ujBgxIk455ZT48z//82Re/v/bcnWcfvKTn8Tbb78dgwYNit/5nd+JMWPGxIwZM3q9dDAF+fq5tGLFimhra4vLL788R3uSW7k6TiNGjIiJEyfGQw89FB9++GFs37497r333igrK4uKiop87d4Bk6vj1NXVFUOHDo1Bg/4zM4YNGxYRES+++GJud+oA25djtCcOtuvvgULYJiDLsqipqYmvfvWrMWnSpIiIaG1tjYiIsrKyHnPLysq6/6y1tTWGDh0an//85z91zqhRo3p9z1GjRnXPSUUuj9PBIp/H6M0334w777wzqqurD/Ru5Fw+jtP1118fhx9+eIwYMSKam5vjb//2b3O1OzmRy2OUZVlcfvnlUV1dHVOmTMn1ruRUrs+lP/3TP40f/ehH8dxzz8U111wTP/jBD+Kqq67K5S7lRC6P04YNGyIi4pZbbok/+7M/i7//+7+Pz3/+83HGGWfEe++9l9P9OpDy+fN76dKlce6558b48eMP9G7kXC6PU0FBQTQ0NERTU1McccQRUVxcHH/1V38VTz/9dFK/eyMit8fprLPOitbW1vjLv/zL2LZtW/zHf/xH3HjjjRER0dLSktP9OpD29RjtiYPp+nsgEbYJuOaaa+Lf/u3f4pFHHun1ZwUFBT2eZ1nWa+y3/facvubvydcZaHJ9nA4G+TpG77zzTvze7/1e/Lf/9t/iyiuv3L9F94N8HKfrrrsumpqa4tlnn43BgwfH7NmzI8uy/V98nuTyGN15553R2dkZCxYsOHAL7ie5Ppfmz58fZ5xxRpx00klx5ZVXxj333BNLly6N9vb2A7MDeZLL47Rz586IiFi4cGFceOGFUVFREQ888EAUFBTEo48+eoD2IPfy9fN78+bN8cwzz8ScOXP2b8H9JJfHKcuyuOqqq2LUqFGxcuXK+Nd//df4xje+Eb//+7+fVLBF5PY4feUrX4m//uu/ju9///tx2GGHxejRo+PYY4+NsrKyGDx48IHbiRw70Mfos77Gvn4d/pOwHeC+/e1vx4oVK+K5556LcePGdY+PHj06IqLXv+ps2bKl+1+RRo8e3f0vZZ8259133+31fX/5y1/2+teogSzXx+lgkK9j9M4778SZZ54Z06ZNi/vuuy8Xu5JT+TpOI0eOjBNOOCHOOeec+NGPfhT19fXx8ssv52KXDrhcH6N//ud/jpdffjmKioqisLAwvvjFL0ZExJQpU+Kyyy7L2X4daP3xc+m0006LiIj/9//+3wHZh3zI9XEaM2ZMRER8+ctf7v7zoqKiOPbYY6O5ufnA71AO5PNceuCBB2LEiBHx9a9//UDvRs7l42fT3//938ePfvSjOP3002Py5MmxePHiGDZsWPz1X/91LnftgMrH+XTJJZdEa2trvP3229He3h633HJL/PKXv4zy8vJc7dYBtT/HaE8cLNffA05O38HLPtu5c2d29dVXZ2PHjs3eeOONPv989OjR2Xe/+93usa6urj7f4F9XV9c955133unzl0f9y7/8S/ecl19+OZk3r+frOP2m1H55VD6P0ebNm7Pjjz8+u/jii/v8DbcDWX+cS7s0NzdnEZE999xzB26HciBfx+itt97KXnvtte7HM888k0VE9thjj2WbNm3K8V7uv/48l/7u7/4ui4jsrbfeOoB7lBv5Ok4dHR1ZUVFRj18eteuXI91777252r0DIt/n0s6dO7Py8vJkfpv9Lvk6TitWrMgGDRrU6zePn3DCCdmf//mf52LXDqj+/Nm0dOnS7LDDDuvztwMPJAfiGP2mz/rlUalefw9UwnaA+p//839mpaWl2Y9//OMev37/o48+6p7zF3/xF1lpaWn2xBNPZK+99lr2x3/8x33+SvZx48Zl//iP/5j95Cc/yc4666w+P+7npJNOylavXp2tXr06+y//5b8k8+vG83mc3nrrraypqSm79dZbs8997nNZU1NT1tTUNOA/WiNfx+jtt9/OvvjFL2ZnnXVWtnnz5h7fKwX5Ok7/8i//kt15551ZU1NT9otf/CL753/+5+yrX/1qdtxxx2W//vWv877feyOff99+08aNG5P6rcj5Ok6rVq3Kbr/99qypqSnbsGFDVldXl40dOzb7+te/nvd93hf5PJ/+9E//NDvqqKOyZ555JvvpT3+azZkzJxs1alT23nvv5XWf91a+/8794z/+YxYR2bp16/K2jwdCvo7TL3/5y2zEiBHZH/7hH2Zr167Nfvazn2XXXnttNmTIkGzt2rV53++9lc/z6c4778waGxuzn/3sZ9ldd92VDRs2LPvhD3+Y1/3dFwfqGLW0tGRNTU3Z//7f/7v7N2k3NTVl7e3t3XNSvv4eqITtABURfT4eeOCB7jk7d+7Mbr755mz06NFZUVFR9v/9f/9f9tprr/X4Or/61a+ya665Jhs+fHg2bNiw7Pd///ez5ubmHnPa29uzb37zm9kRRxyRHXHEEdk3v/nNAf8varvk8zhddtllfX6vgX6XLV/H6IEHHtjt90pBvo7Tv/3bv2VnnnlmNnz48KyoqCg75phjsurq6mzz5s352tV9ls+/b78ptbDN13FqbGzMpk6dmpWWlmbFxcXZl770pezmm2/OPvzww3zt6n7J5/m0bdu27Dvf+U42atSo7IgjjsjOPvvs7N///d/zsZv7Jd9/5/74j/84q6yszPVu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9tlnY8qUKTFkyJA+tykqKoqioqJe46WlpX5Ic0CUlJQ4lzggnEscSM4nDhTnEgfKoEF+jVF/cNTzoKamJu6///5YtmxZrF+/PubPnx/Nzc1RXV0dEZ/cbZ09e3b3/Orq6njrrbeipqYm1q9fH8uWLYulS5fGtdde21+7AAAAMGC5Y5sHs2bNivb29li0aFG0tLTEpEmTor6+PiZMmBARES0tLT0+07a8vDzq6+tj/vz5cffdd8fYsWPjjjvuiAsvvLC/dgEAAGDAErZ5ctVVV8VVV13V5589+OCDvcbOOOOM+MlPfrLP36+oqChuvvnmPl+eDHvDucSB4lziQHI+caA4lzhQnEv9qyDz+6gBAABImPfYAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2CZs8eLFUV5eHsXFxVFRURErV6781PnPP/98VFRURHFxcRx77LFxzz335GmlDHR7cy498cQTcc4558QXvvCFKCkpiWnTpsUzzzyTx9UykO3tz6VdXnrppSgsLIxTTjkltwskGXt7LnV1dcXChQtjwoQJUVRUFMcdd1wsW7YsT6tloNvb82n58uVx8sknx2GHHRZjxoyJK664Itrb2/O0WgaqF154IS644IIYO3ZsFBQUxFNPPfWZ27j+zh9hm6i6urqYN29eLFy4MJqammL69OkxY8aMHp+H+5s2btwY5513XkyfPj2amprixhtvjLlz58bjjz+e55Uz0OztufTCCy/EOeecE/X19dHY2BhnnnlmXHDBBdHU1JTnlTPQ7O25tEtHR0fMnj07vva1r+VppQx0+3IuXXTRRfFP//RPsXTp0vjZz34WjzzySJx44ol5XDUD1d6eTy+++GLMnj075syZE6+//no8+uij8corr8SVV16Z55Uz0Hz44Ydx8sknx1133bVH811/51lGkk499dSsurq6x9iJJ56Y3XDDDX3O/1//639lJ554Yo+xb33rW9lpp52WszWShr09l/ry5S9/Obv11lsP9NJIzL6eS7Nmzcr+7M/+LLv55puzk08+OYcrJBV7ey79wz/8Q1ZaWpq1t7fnY3kkZm/Pp7/8y7/Mjj322B5jd9xxRzZu3LicrZH0RET25JNPfuoc19/55Y5tgrZt2xaNjY1RVVXVY7yqqipWrVrV5zarV6/uNf/cc8+NNWvWxMcff5yztTKw7cu59Nt27twZ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atSo3c6rra2N0tLS7sf48eP3e80AAAApELY5NnLkyBg8eHCvu7NbtmzpdRf3t9XV1cWcOXPib/7mb+Lss8/+1LkLFiyIjo6O7semTZv2e+0AAAApELY5NnTo0KioqIiGhoYe4w0NDVFZWbnb7R555JG4/PLL4+GHH47zzz//M79PUVFRlJSU9HgAAAAcCrzHNg9qamri0ksvjSlTpsS0adPivvvui+bm5qiuro6IT+62vv322/HQQw9FxCdRO3v27PjhD38Yp512Wvfd3mHDhkVpaWm/7QcAAMBAJGzzYNasWdHe3h6LFi2KlpaWmDRpUtTX18eECRMiIqKlpaXHZ9ree++9sX379rj66qvj6quv7h6/7LLL4sEHH8z38gEAAAY0n2N7kPI5WgAAkD+uv/uX99gCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2ObJ4sWLo7y8PIqLi6OioiJWrlz5qfOff/75qKioiOLi4jj22GPjnnvuydNKAQAA0iJs86Curi7mzZsXCxcujKamppg+fXrMmDEjmpub+5y/cePGOO+882L69OnR1NQUN954Y8ydOzcef/zxPK8cAABg4CvIsizr70Uc7KZOnRqTJ0+OJUuWdI9NnDgxZs6cGbW1tb3mX3/99bFixYpYv35991h1dXW8+uqrsXr16j36np2dnVFaWhodHR1RUlKy/zsBAADsluvv/lXY3ws42G3bti0aGxvjhhtu6DFeVVUVq1at6nOb1atXR1VVVY+xc889N5YuXRoff/xxDBkypNc2XV1d0dXV1f28o6MjIj75CwYAAOTWrutu9w37h7DNsba2ttixY0eUlZX1GC8rK4vW1tY+t2ltbe1z/vbt26OtrS3GjBnTa5va2tq49dZbe42PHz9+P1YPAADsjfb29igtLe3vZRxyhG2eFBQU9HieZVmvsc+a39f4LgsWLIiampru5++//35MmDAhmpub/cViv3R2dsb48eNj06ZNXlbDfnEucSA5nzhQnEscKB0dHXH00UfH8OHD+3sphyRhm2MjR46MwYMH97o7u2XLll53ZXcZPXp0n/MLCwtjxIgRfW5TVFQURUVFvcZLS0v9kOaAKCkpcS5xQDiXOJCcTxwoziUOlEGD/H7e/uCo59jQoUOjoqIiGhoaeow3NDREZWVln9tMmzat1/xnn302pkyZ0uf7awEAAA5lwjYPampq4v77749ly5bF+vXrY/78+dHc3BzV1dUR8cnLiGfPnt09v7q6Ot56662oqamJ9evXx7Jly2Lp0qVx7bXX9tcuAAAADFheipwHs2bNivb29li0aFG0tLTEpEmTor6+PiZMmBARES0tLT0+07a8vDzq6+tj/vz5cffdd8fYsWPjjjvuiAsvvHCPv2dRUVHcfPPNfb48GfaGc4kDxbnEgeR84kBxLnGgOJf6l8+xBQAAIGleigwAAEDShC0AAABJE7YAAAAkTdgCAACQNGGbsMWLF0d5eXkUFxdHRUVFrFy58lPnP//881FRURHFxcVx7LHHxj333JOnlTLQ7c259MQTT8Q555wTX/jCF6KkpCSmTZsWzzzzTB5Xy0C2tz+XdnnppZeisLAwTjnllNwukGTs7bnU1dUVCxcujAkTJkRRUVEcd9xxsWzZsjytloFub8+n5cuXx8knnxyHHXZYjBkzJq644opob2/P02oZqF544YW44IILYuzYsVFQUBBPPfXUZ27j+jt/hG2i6urqYt68ebFw4cJoamqK6dOnx4wZM3p8bNBv2rhxY5x33nkxffr0aGpqihtvvDHmzp0bjz/+eJ5XzkCzt+fSCy+8EOecc07U19dHY2NjnHnmmXHBBRdEU1NTnlfOQLO359IuHR0dMXv27Pja176Wp5Uy0O3LuXTRRRfFP/3TP8XSpUvjZz/7WTzyyCNx4okn5nHVDFR7ez69+OKLMXv27JgzZ068/vrr8eijj8Yrr7wSV155ZZ5XzkDz4Ycfxsknnxx33XXXHs13/Z1nGUk69dRTs+rq6h5jJ554YnbDDTf0Of9//a//lZ144ok9xr71rW9lp512Ws7WSBr29lzqy5e//OXs1ltvPdBLIzH7ei7NmjUr+7M/+7Ps5ptvzk4++eQcrpBU7O259A//8A9ZaWlp1t7eno/lkZi9PZ/+8i//Mjv22GN7jN1xxx3ZuHHjcrZG0hMR2ZNPPvmpc1x/55c7tgnatm1bNDY2RlVVVY/xqqqqWLVqVZ/brF69utf8c889N9asWRMff/xxztbKwLYv59Jv27lzZ2zdujWGDx+eiyWSiH09lx544IF488034+abb871EknEvpxLK1asiClTpsT3vve9OOqoo+KEE06Ia6+9Nn71q1/lY8kMYPtyPlVWVsbmzZujvr4+siyLd999Nx577LE4//zz87FkDiKuv/OrsL8XwN5ra2uLHTt2RFlZWY/xsrKyaG1t7XOb1tbWPudv37492traYsyYMTlbLwPXvpxLv+373/9+fPjhh3HRRRflYokkYl/OpZ///Odxww03xMqVK6Ow0P+O+MS+nEsbNmyIF198MYqLi+PJJ5+Mtra2uOqqq+K9997zPttD3L6cT5WVlbF8+fKYNWtW/PrXv47t27fH17/+9bjzzjvzsWQOIq6/88sd24QVFBT0eJ5lWa+xz5rf1ziHnr09l3Z55JFH4pZbbom6uroYNWpUrpZHQvb0XNqxY0dccsklceutt8YJJ5yQr+WRkL35ubRz584oKCiI5cuXx6mnnhrnnXde3H777fHggw+6a0tE7N35tG7dupg7d27cdNNN0djYGE8//XRs3Lgxqqur87FUDjKuv/PHP5EnaOTIkTF48OBe/9K4ZcuWXv8qtMvo0aP7nF9YWBgjRozI2VoZ2PblXNqlrq4u5syZE48++micffbZuVwmCdjbc2nr1q2xZs2aaGpqimuuuSYiPomTLMuisLAwnn322TjrrLPysnYGln35uTRmzJg46qijorS0tHts4sSJkWVZbN68OY4//vicrpmBa1/Op9ra2jj99NPjuuuui4iIk046KQ4//PCYPn163Hbbbe6yscdcf+eXO7YJGjp0aFRUVERDQ0OP8YaGhqisrOxzm2nTpvWa/+yzz8aUKVNiyJAhOVsrA9u+nEsRn9ypvfzyy+Phhx/2niMiYu/PpZKSknjttddi7dq13Y/q6ur40pe+FGvXro2pU6fma+kMMPvyc+n000+Pd955Jz744IPusTfeeCMGDRoU48aNy+l6Gdj25Xz66KOPYtCgnpfIgwcPjoj/vNsGe8L1d5710y+tYj/96Ec/yoYMGZItXbo0W7duXTZv3rzs8MMPz37xi19kWZZlN9xwQ3bppZd2z9+wYUN22GGHZfPnz8/WrVuXLV26NBsyZEj22GOP9dcuMEDs7bn08MMPZ4WFhdndd9+dtbS0dD/ef//9/toFBoi9PZd+m9+KzC57ey5t3bo1GzduXPZHf/RH2euvv549//zz2fHHH59deeWV/bULDCB7ez498MADWWFhYbZ48eLszTffzF588cVsypQp2amnntpfu8AAsXXr1qypqSlramrKIiK7/fbbs6ampuytt97Kssz1d38Ttgm7++67swkTJmRDhw7NJk+enD3//PPdf3bZZZdlZ5xxRo/5P/7xj7Pf+Z3fyYYOHZodc8wx2ZIlS/K8YgaqvTmXzjjjjCwiej0uu+yy/C+cAWdvfy79JmHLb9rbc2n9+vXZ2WefnQ0bNiwbN25cVlNTk3300Ud5XjUD1d6eT3fccUf25S9/ORs2bFg2ZsyY7Jvf/Ga2efPmPK+agea555771Gsg19/9qyDLvKYCAACAdHmPLQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACTt/wcLAF7/XlacegAAAABJRU5ErkJggg==", + "text/html": [ + "\n", + "
\n", + "
\n", + " Figure\n", + "
\n", + " \n", + "
\n", + " " + ], + "text/plain": [ + "Canvas(toolbar=Toolbar(toolitems=[('Home', 'Reset original view', 'home', 'home'), ('Back', 'Back to previous …" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'downwelling_shortwave'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'downwelling_shortwave'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/SFCCLDGRID/15swfcldgrid1long.c1.ipynb b/VAPs/quicklook/SFCCLDGRID/15swfcldgrid1long.c1.ipynb new file mode 100644 index 00000000..5df1fe58 --- /dev/null +++ b/VAPs/quicklook/SFCCLDGRID/15swfcldgrid1long.c1.ipynb @@ -0,0 +1,339 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# 15SWFCLDGRID1LONG.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/sfccldgrid) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = '15swfcldgrid1long'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2009-11-25', 'facility': 'N1', 'site': 'sgp', 'start_date': '1997-01-01'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'N1' )\n", + "\n", + "date_start = '2009-11-23'\n", + "date_end = '2009-11-25'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['cloudfraction', 'cf_cloudfraction', 'tswfluxdn']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'cloudfraction'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'cloudfraction'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/SFCCLDGRID/SFCCLDGRID_tutorial.ipynb b/VAPs/quicklook/SFCCLDGRID/SFCCLDGRID_tutorial.ipynb new file mode 100644 index 00000000..e04e7387 --- /dev/null +++ b/VAPs/quicklook/SFCCLDGRID/SFCCLDGRID_tutorial.ipynb @@ -0,0 +1,838 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# 15SWFCLDGRID1LONG.C1 Notebook\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/sfccldgrid) for more information about this vap." + ] + }, + { + "cell_type": "markdown", + "id": "97097763", + "metadata": {}, + "source": [ + "In this notebook, we demonstrate the workflow to explore ARM vap data (using 15swfcldgrid1long as an example.) Value-added products (VAPs) are higher-order data products that have been analyzed and processed to ease scientists’ use of ARM data in atmospheric research and global climate models. \n", + "Here is the main content we will cover." + ] + }, + { + "cell_type": "markdown", + "id": "eddec40f", + "metadata": {}, + "source": [ + "# Table of Content\n", + "## Access the data\n", + "* How to retrieve the data\n", + "* Data path and file name conventions\n", + "* Load data\n", + "## Explore the data\n", + "* NetCDF Data structure\n", + "* Xarray essentials\n", + "* Xarray Variable\n", + "## Plot the data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "import random\n", + "\n", + "import glob\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "2372d5be", + "metadata": {}, + "source": [ + "## Access the data" + ] + }, + { + "cell_type": "markdown", + "id": "5b0d9684", + "metadata": {}, + "source": [ + "### How to retrieve the data\n", + "We assume the path \"/data/archive\" is available where you are running this notebook. You can use `os.path.exists(\"/data/archive\")` to verify if the path exists at your machine.\n", + "\n", + "\n", + "### Data path and file name conventions\n", + "There several common terminologies regarding ARM data, for example, data-stream-name, data-level, site, facility, instrument, etc. (For more details, please see [ARM Data File Standards Version 1.2](https://www.google.com/search?q=arm+datastream+facility+cite+definition&rlz=1C1GCEJ_enUS1029US1029&ei=hb41ZICrBPukqtsPvMODIA&ved=0ahUKEwjAgeiS0aL-AhV7kmoFHbzhAAQQ4dUDCBA&uact=5&oq=arm+datastream+facility+cite+definition&gs_lcp=Cgxnd3Mtd2l6LXNlcnAQAzIFCAAQogQyBQgAEKIEMgUIABCiBDIFCAAQogQyBQgAEKIEOgoIABBHENYEELADOgoIIRCgARDDBBAKSgQIQRgAUJ4DWIoOYO0PaAFwAXgAgAGMAYgBuQmSAQMyLjmYAQCgAQHIAQjAAQE&sclient=gws-wiz-serp).) \n", + "\n", + "For example, this notebook is called `15swfcldgrid1long.c1`, where `15swfcldgrid1long` is the \"datastream name\", and the `{process.ds_class_level}}` is the \"data level\".\n", + "\n", + "This datastream also contains site `sgp` and facility `N1`. (Note: individual datastream might have multiple site-facility pairs.)\n", + "In such a case, the data of this data-stream is stored at `/data/archive/sgp/sgp15swfcldgrid1longN1.c1`, which is in the format of `//.`. We can use the following method to assign the data-stream directory `datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )`\n", + "\n", + "The data files under datastream_dir also follows naming conventions. But once reach the datastream_dir level, the most import file naming convention to differentiate the files is \"yyyyMMdd.hhmmss\", which comes handy to filter out files based on datetime. For example, we can use `glob.glob(f'{datastream_dir}/*.200709*.*')` to filter files in 2007 September.\n", + "\n", + "(Note: refer to the https://adc.arm.gov/discovery/#/ and https://adc.arm.gov/solr8/metadata/select API to explore ARM datastream and assoicated available sites and facilities\n", + "\n", + "Please see the following examples in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d7e9eb85", + "metadata": {}, + "outputs": [], + "source": [ + "# Verify if DATA_DIR path exists\n", + "DATA_DIR = \"/data/archive\"\n", + "os.path.exists(DATA_DIR)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "586993fd", + "metadata": {}, + "outputs": [], + "source": [ + "# Speicify datastream_dir following the path conventions and check its existence\n", + "DATASTREAM_NAME = \"15swfcldgrid1long\"\n", + "DATA_LEVEL = \"c1\"\n", + "site = \"sgp\"\n", + "facility = \"N1\"\n", + "datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )\n", + "print(datastream_dir)\n", + "print(os.path.exists(datastream_dir))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0742f7c1", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: list 5 (random) files under datastream_dir\n", + "files = os.listdir(datastream_dir)\n", + "files[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39b98a36", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: get most recent file\n", + "list_of_files = glob.glob(f\"{datastream_dir}/*\") # * means all if need specific format then *.csv\n", + "latest_file = max(list_of_files, key=os.path.getctime)\n", + "latest_file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "902d514e", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus sort datastream files based on datetime\n", + "files = os.listdir(datastream_dir)\n", + "file_sorted = files.copy()\n", + "file_sorted.sort() \n", + "print(file_sorted[:5])\n", + "\n", + "# to reverse\n", + "file_sorted_reverse = files.copy() \n", + "file_sorted_reverse.sort(reverse=True)\n", + "print(file_sorted_reverse[:5])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec5923b2", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: pattern matching\n", + "# filter the 200709** files under datastream_dir\n", + "files_filter = glob.glob(f'{datastream_dir}/*.200709*.*')\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c4a4aa18", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: use armnotebook_utils.file_filter (TODO) to filter files based on datastream info \n" + ] + }, + { + "cell_type": "markdown", + "id": "cfeb9efc", + "metadata": {}, + "source": [ + "### Load data\n", + "The arm data is stored in [NetCDF](https://en.wikipedia.org/wiki/NetCDF#:~:text=%22NetCDF%20(network%20Common%20Data%20Form,format%20for%20representing%20scientific%20data.) format. We can use xarray's `open_dataset` method to load single file, or `open_mfdataset` to open multiple files. (Note: the latter will still return a single xarray dataset object by combining multiple files.)\n", + "\n", + "Note: open_dataset keeps the file handle open and lazy loads its contents. All parameters are passed directly to open_dataset. It is a preferable method over load_dataset for memory-effiency. (more details, please see [xarray.open_dataset](https://docs.xarray.dev/en/stable/generated/xarray.open_dataset.html))\n", + "\n", + "See the following example in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a440a329", + "metadata": {}, + "outputs": [], + "source": [ + "# open single file\n", + "full_path = latest_file\n", + "print(full_path)\n", + "ds_single = xr.open_dataset(full_path)\n", + "ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0143a3d", + "metadata": {}, + "outputs": [], + "source": [ + "print(ds_single)\n", + "print(type(ds_single))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c0f8939", + "metadata": {}, + "outputs": [], + "source": [ + "# open multiple files\n", + "n_files = 3\n", + "full_paths = [os.path.join(datastream_dir, f_path) for f_path in file_sorted_reverse[:n_files]]\n", + "print(full_paths)\n", + "try: # Note: sometimes multiple files cannot be merged, so we used try except here.\n", + " ds_mutiple = xr.open_mfdataset(full_paths)\n", + " # ds_mutiple\n", + " print(type(ds_mutiple))\n", + " print(ds_mutiple)\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "3fa59943", + "metadata": {}, + "source": [ + "## Explore the data" + ] + }, + { + "cell_type": "markdown", + "id": "571f69b9", + "metadata": {}, + "source": [ + "### NetCDF Data structure \n", + "\n", + "\n", + "(If you are confident with NetCDF basics and xarray essentials, feel free to skip this session.)\n", + "Before we dive into data exploration, there are some eseential concpets we should be familiar with (shown below): \n", + "* Dataset\n", + "* Data array\n", + "* Variable\n", + "* Dimenssion\n", + "* Coordinate\n", + "* Data Type\n", + "* Meta Data (Attributes)\n", + "\n", + "We will not go into details about NetCDF basics and here are some references you might find helpful\n", + "* [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#)\n", + "* [Network Common Data Form (NetCDF)](https://www.unidata.ucar.edu/software/netcdf/) and [A Brief History of (netCDF) Time](https://www.unidata.ucar.edu/software/netcdf/time/recs.html)\n", + "\n", + "\n", + "\n", + "### Xarray essentials\n", + "Earlier we introduced the xarray pacakge and used `open_dataset` and `open_mfdataset` to retrieve NetCDF data file as an xarray Dataset object. Recall that you can review the data in a notebook by using `print(ds)` or `ds`. \n", + "\n", + "Using xarray to retrieve the aforementioned NetCDF basics is straightforwared. In general,\n", + "* Dataset: `ds`\n", + "* Data array: `ds.variables`\n", + "* Variable: `ds.ds.variables`\n", + "* Dimenssion: `ds.dims`\n", + "* Coordinate: `ds.coords`\n", + "* Data Type: `type`\n", + "* Meta Data (Attributes)\n", + "\n", + "Also, here are some references if you are new to xarray\n", + "* [Xarray in 45 minutes](https://tutorial.xarray.dev/overview/xarray-in-45-min.html)\n", + "* [Handling NetCDF Files using XArray for Absolute Beginners](https://towardsdatascience.com/handling-netcdf-files-using-xarray-for-absolute-beginners-111a8ab4463f)\n", + "\n", + "Try the following commands in action\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b3eac323", + "metadata": {}, + "outputs": [], + "source": [ + "ds = ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77ecf85d", + "metadata": {}, + "outputs": [], + "source": [ + "# Dataset \n", + "ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25e7de09", + "metadata": {}, + "outputs": [], + "source": [ + "# Data array, Variable\n", + "# Note: the info can be overwhelming. Do not attempt to grasp everything at the first glance.\n", + "ds.variables" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c41b67e", + "metadata": {}, + "outputs": [], + "source": [ + "# dimenssions\n", + "ds.dims" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "156f1dfc", + "metadata": {}, + "outputs": [], + "source": [ + "# coordinates\n", + "ds.coords" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "277d6064", + "metadata": {}, + "outputs": [], + "source": [ + "# Meta Data (Attributes)\n", + "ds.attrs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d334681f", + "metadata": {}, + "outputs": [], + "source": [ + "# type\n", + "print(type(ds))\n", + "print(type(ds.variables))\n", + "print(type(ds.dims))\n", + "print(type(ds.coords))\n", + "print(type(ds.attrs))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "643399d6", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: retrieve variable names only\n", + "list(ds.variables)" + ] + }, + { + "cell_type": "markdown", + "id": "1cf36878", + "metadata": {}, + "source": [ + "#### Discussion: variable vs. coordinates vs. dimenssions. \n", + "From [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#):\n", + "\n", + "* Dimession: A dimension may be used to represent a real physical dimension, for example, time, latitude, longitude, or height. A dimension might also be used to index other quantities, for example station or model-run-number.\n", + "A netCDF dimension has both a name and a length. A dimension length is an arbitrary positive integer, except that one dimension in a netCDF dataset can have the length UNLIMITED.\n", + "\n", + "* Variables: Variables are used to store the bulk of the data in a netCDF dataset. A variable represents an array of values of the same type. A scalar value is treated as a 0-dimensional array. A variable has a name, a data type, and a shape described by its list of dimensions specified when the variable is created. A variable may also have associated attributes, which may be added, deleted or changed after the variable is created.\n", + "\n", + "* Coordinate (Variables): It is legal for a variable to have the same name as a dimension. Such variables have no special meaning to the netCDF library. However there is a convention that such variables should be treated in a special way by software using this library.\n", + "A variable with the same name as a dimension is called a coordinate variable. It typically defines a physical coordinate corresponding to that dimension.\n", + "\n", + "\n", + "Tips: By definition, coordinates and dimenssions are also variable. For example ds.time is a coordinate but it is also a special variable. But in practice, when we talk about variable, it implies regular/non-coordinate variable. We will use this convention for the remaining notebook. " + ] + }, + { + "cell_type": "markdown", + "id": "d47120f9", + "metadata": {}, + "source": [ + "### Xarray Variable\n", + "\n", + "\n", + "We can use `ds.variables` to access varialbes of a dataset. But many times viewing print-out the whole datasets can be overwhemling. Instead, we would work on the individual variable (also called Xarray data array.) using the `ds[var_name]` syntax, where `var_name` is the name of the variable. See the following examples in action.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5a57e136", + "metadata": {}, + "outputs": [], + "source": [ + "print(type(ds[\"time\"]))\n", + "ds[\"time\"]" + ] + }, + { + "cell_type": "markdown", + "id": "51ec643e", + "metadata": {}, + "source": [ + "#### Variable properties\n", + "For individual varible, the following properties are mostly used with the assocaited xarray method to retrieve them. (assuming var = ds[var_name])\n", + "* name: `var.name`\n", + "* data content: `var.data`\n", + "* attributes: `var.attrs`\n", + "* dimenstions: `var.dims`\n", + "* data type: `var.data.dtype`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a632a525", + "metadata": {}, + "outputs": [], + "source": [ + "var_name = \"time\"\n", + "var = ds[var_name]\n", + "\n", + "print(\"var.name: \\n\", var.name, \"\\n\")\n", + "print(\"var.data: \\n\", var.data, \"\\n\")\n", + "print(\"var.attrs: \\n\", var.attrs, \"\\n\")\n", + "print(\"var.dims: \\n\", var.dims, \"\\n\")\n", + "print(\"var.data.dtype: \\n\", var.data.dtype, \"\\n\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b256b07f", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: indivdual variable info table (collect and display variable info as a tabular format)\n", + "df_info = pd.DataFrame()\n", + "df_info[\"var_name\"] = list(ds.variables)\n", + "df_info[\"dims\"] = df_info.var_name.apply(lambda x: ds[x].dims)\n", + "df_info[\"is_dim\"] = df_info.var_name.apply(lambda x: x in ds.dims)\n", + "df_info[\"n_dim\"] = df_info.var_name.apply(lambda x: len(ds[x].dims))\n", + "df_info[\"attrs\"] = df_info.var_name.apply(lambda x: ds[x].attrs)\n", + "df_info[\"dtype\"] = df_info.var_name.apply(lambda x: ds[x].data.dtype)\n", + "df_info" + ] + }, + { + "cell_type": "markdown", + "id": "e6a36b96", + "metadata": {}, + "source": [ + "### Data cleaning/Preprocessing (skipped)\n", + "Data cleaning and preprocessing is an important stage in the the data analysis pipeline. However it will be out of the scope of this notebook. For more information about data cleaning and preprocessing basics, here are some references. \n", + "* [Xarray Fundamentals](https://earth-env-data-science.github.io/lectures/xarray/xarray.html)\n", + "* [Xarray Tutorial — Pangeo Gallery documentation](http://gallery.pangeo.io/repos/pangeo-data/pangeo-tutorial-gallery/xarray.html)\n", + "* [Pythonic Data Cleaning With pandas and NumPy](https://realpython.com/python-data-cleaning-numpy-pandas/)\n", + "* [Pandas - Cleaning Data](https://www.w3schools.com/python/pandas/pandas_cleaning.asp)" + ] + }, + { + "cell_type": "markdown", + "id": "ae6e1a69", + "metadata": {}, + "source": [ + "### Plotting\n", + "Data visualization (or plotting) is another important data analysis topic and deserves its own discussion. Here in this notebook we will only demonstrate basic tool for simple data visualization tasks.\n", + "\n", + "Here are some reference you might find useful:\n", + "* xarray plotting: https://docs.xarray.dev/en/stable/user-guide/plotting.html\n", + "* Atmospheric Community Toolkit (ACT): https://arm-doe.github.io/ACT/index.html\n", + "\n", + "Note: this notebook is auto-generated using a template. It uses a general idea to select variable(s) to plot and is not customized to each indivdual notebook. Feel free to change the variables in intrrests, especially certain figure is failed to plot." + ] + }, + { + "cell_type": "markdown", + "id": "7e417fb2", + "metadata": {}, + "source": [ + "#### 1-dimenssional basic time series plot\n", + "\n", + "For the following plot we would like to find variables such that\n", + "* it has one and only one dimession\n", + "* \"time\" is its coordinate variable\n", + "* it is not a dimenssion itself,\n", + "* it is not a special variable with substrings within [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "\n", + "Please see the following example in action. (For more details about pandas filtering, please see the following references.)\n", + "* [pandas: multiple conditions while indexing data frame](https://stackoverflow.com/questions/22591174/pandas-multiple-conditions-while-indexing-data-frame-unexpected-behavior)\n", + "* [pandas.DataFrame.apply](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.apply.html)\n", + "* [How to test if a string contains one of the substrings in a list...](https://stackoverflow.com/questions/26577516/how-to-test-if-a-string-contains-one-of-the-substrings-in-a-list-in-pandas" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30edac2d", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==1, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter = df_info[(df_info.n_dim == 1) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4777a659", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_1d = df_filter.var_name.values[0]\n", + " var_1d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb5ae985", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " ds[var_1d].plot()\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "9e41066e", + "metadata": {}, + "source": [ + "#### 2-dimenssional basic plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09bd4adc", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==2, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_2 = df_info[(df_info.n_dim == 2) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2cdea1a9", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_2d = df_filter_2.var_name.values[0]\n", + " var_2d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5f48879", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " print(ds[var_2d].dims)\n", + " # ds[var_2d].plot()\n", + "\n", + " # conventionally, use \"time\" as x-axis\n", + " ds[var_2d].plot(x=\"time\")\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "7554b2a6", + "metadata": {}, + "source": [ + "#### qc-plotting (optional)\n", + "\n", + "Note: act qc plotting has more strict requirement, one of them is the associated qc variable need to have \"flag_masks\" attributes. Which is added by using ds = act.io.armfiles.read_netcdf(files_list), then ds.clean.cleanup()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71c2096f", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " ds_act = act.io.armfiles.read_netcdf(full_path)\n", + " print(type(ds_act))\n", + " ds_act.clean.cleanup()\n", + "\n", + " # or \n", + " # ds.clean.cleanup()\n", + "except Exception as e:\n", + " print(\"ERROR\", e)\n", + " ds_act = ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "844f8505", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter valid variables for ACT qc plotting\n", + "condition = (\"qc_\" + df_info.var_name).apply(lambda x: ds_act[x].attrs.get(\"flag_masks\") is not None \n", + " if x in list(ds.variables) else False)\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_3 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings))) &\n", + " condition\n", + " ]\n", + "df_filter_3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd38cc6", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "try:\n", + " qc_variable = df_filter_3.var_name.values[0]\n", + " print(qc_variable)\n", + "except Exception as e:\n", + " print(e)\n", + " \n", + "try:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + " qc_ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "d76e4d27", + "metadata": {}, + "source": [ + "#### bonus: choose variables to plot from a dropdown menu " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb733804", + "metadata": {}, + "outputs": [], + "source": [ + "# Valid variables filtering\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_4 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82d5c20d", + "metadata": {}, + "outputs": [], + "source": [ + "# example 1: using xarray plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2fc9aaa4", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# fig, ax = plt.subplots(figsize=(10, 4))\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "# fig.clear() # Remove old lines from plot and plot new one\n", + "# if len(ds[var].dims)==2:\n", + "# ds[var].plot(x=\"time\", add_colorbar=False)\n", + "# else:\n", + "# ds[var].plot()\n", + "# plt.grid()\n", + "# plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fabdd802", + "metadata": {}, + "outputs": [], + "source": [ + "# example 2: using act plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "962b4186", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "\n", + "\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "\n", + "# i_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + "# i_display.add_subplots((1,), figsize=(10, 4))\n", + "# ax = i_display.plot(var, subplot_index=(0,), set_title=f\"{var} ({ds_act[var].attrs['long_name']})\",)\n", + "\n", + "# ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "# ax.grid()\n", + "# plt.show()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/SFCCLDGRID/sfccldgrid2longcaracena.c1.ipynb b/VAPs/quicklook/SFCCLDGRID/sfccldgrid2longcaracena.c1.ipynb new file mode 100644 index 00000000..c3b7dceb --- /dev/null +++ b/VAPs/quicklook/SFCCLDGRID/sfccldgrid2longcaracena.c1.ipynb @@ -0,0 +1,339 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# SFCCLDGRID2LONGCARACENA.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/sfccldgrid) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'sfccldgrid2longcaracena'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2020-06-01', 'facility': 'N1', 'site': 'sgp', 'start_date': '2011-10-21'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'N1' )\n", + "\n", + "date_start = '2020-05-29'\n", + "date_end = '2020-05-31'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['downwelling_shortwave', 'source_central_facility_downwelling_shortwave', 'clearsky_downwelling_shortwave']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'downwelling_shortwave'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'downwelling_shortwave'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/SFCCLDGRID/sfccldgrid2longstation.c1.ipynb b/VAPs/quicklook/SFCCLDGRID/sfccldgrid2longstation.c1.ipynb new file mode 100644 index 00000000..bf1a8b6b --- /dev/null +++ b/VAPs/quicklook/SFCCLDGRID/sfccldgrid2longstation.c1.ipynb @@ -0,0 +1,339 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# SFCCLDGRID2LONGSTATION.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/sfccldgrid) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'sfccldgrid2longstation'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2020-06-01', 'facility': 'N1', 'site': 'sgp', 'start_date': '2009-01-01'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'N1' )\n", + "\n", + "date_start = '2020-05-29'\n", + "date_end = '2020-05-31'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['swdn', 'cswdn', 'lwdn']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'swdn'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'swdn'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/SHALLOWCUMULUS/SHALLOWCUMULUS_tutorial.ipynb b/VAPs/quicklook/SHALLOWCUMULUS/SHALLOWCUMULUS_tutorial.ipynb new file mode 100644 index 00000000..01eddf79 --- /dev/null +++ b/VAPs/quicklook/SHALLOWCUMULUS/SHALLOWCUMULUS_tutorial.ipynb @@ -0,0 +1,838 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# SHALLOWCUMULUS.C1 Notebook\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/shallowcumulus) for more information about this vap." + ] + }, + { + "cell_type": "markdown", + "id": "97097763", + "metadata": {}, + "source": [ + "In this notebook, we demonstrate the workflow to explore ARM vap data (using shallowcumulus as an example.) Value-added products (VAPs) are higher-order data products that have been analyzed and processed to ease scientists’ use of ARM data in atmospheric research and global climate models. \n", + "Here is the main content we will cover." + ] + }, + { + "cell_type": "markdown", + "id": "eddec40f", + "metadata": {}, + "source": [ + "# Table of Content\n", + "## Access the data\n", + "* How to retrieve the data\n", + "* Data path and file name conventions\n", + "* Load data\n", + "## Explore the data\n", + "* NetCDF Data structure\n", + "* Xarray essentials\n", + "* Xarray Variable\n", + "## Plot the data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "import random\n", + "\n", + "import glob\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "2372d5be", + "metadata": {}, + "source": [ + "## Access the data" + ] + }, + { + "cell_type": "markdown", + "id": "5b0d9684", + "metadata": {}, + "source": [ + "### How to retrieve the data\n", + "We assume the path \"/data/archive\" is available where you are running this notebook. You can use `os.path.exists(\"/data/archive\")` to verify if the path exists at your machine.\n", + "\n", + "\n", + "### Data path and file name conventions\n", + "There several common terminologies regarding ARM data, for example, data-stream-name, data-level, site, facility, instrument, etc. (For more details, please see [ARM Data File Standards Version 1.2](https://www.google.com/search?q=arm+datastream+facility+cite+definition&rlz=1C1GCEJ_enUS1029US1029&ei=hb41ZICrBPukqtsPvMODIA&ved=0ahUKEwjAgeiS0aL-AhV7kmoFHbzhAAQQ4dUDCBA&uact=5&oq=arm+datastream+facility+cite+definition&gs_lcp=Cgxnd3Mtd2l6LXNlcnAQAzIFCAAQogQyBQgAEKIEMgUIABCiBDIFCAAQogQyBQgAEKIEOgoIABBHENYEELADOgoIIRCgARDDBBAKSgQIQRgAUJ4DWIoOYO0PaAFwAXgAgAGMAYgBuQmSAQMyLjmYAQCgAQHIAQjAAQE&sclient=gws-wiz-serp).) \n", + "\n", + "For example, this notebook is called `shallowcumulus.c1`, where `shallowcumulus` is the \"datastream name\", and the `{process.ds_class_level}}` is the \"data level\".\n", + "\n", + "This datastream also contains site `sgp` and facility `C1`. (Note: individual datastream might have multiple site-facility pairs.)\n", + "In such a case, the data of this data-stream is stored at `/data/archive/sgp/sgpshallowcumulusC1.c1`, which is in the format of `//.`. We can use the following method to assign the data-stream directory `datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )`\n", + "\n", + "The data files under datastream_dir also follows naming conventions. But once reach the datastream_dir level, the most import file naming convention to differentiate the files is \"yyyyMMdd.hhmmss\", which comes handy to filter out files based on datetime. For example, we can use `glob.glob(f'{datastream_dir}/*.200709*.*')` to filter files in 2007 September.\n", + "\n", + "(Note: refer to the https://adc.arm.gov/discovery/#/ and https://adc.arm.gov/solr8/metadata/select API to explore ARM datastream and assoicated available sites and facilities\n", + "\n", + "Please see the following examples in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d7e9eb85", + "metadata": {}, + "outputs": [], + "source": [ + "# Verify if DATA_DIR path exists\n", + "DATA_DIR = \"/data/archive\"\n", + "os.path.exists(DATA_DIR)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "586993fd", + "metadata": {}, + "outputs": [], + "source": [ + "# Speicify datastream_dir following the path conventions and check its existence\n", + "DATASTREAM_NAME = \"shallowcumulus\"\n", + "DATA_LEVEL = \"c1\"\n", + "site = \"sgp\"\n", + "facility = \"C1\"\n", + "datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )\n", + "print(datastream_dir)\n", + "print(os.path.exists(datastream_dir))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0742f7c1", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: list 5 (random) files under datastream_dir\n", + "files = os.listdir(datastream_dir)\n", + "files[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39b98a36", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: get most recent file\n", + "list_of_files = glob.glob(f\"{datastream_dir}/*\") # * means all if need specific format then *.csv\n", + "latest_file = max(list_of_files, key=os.path.getctime)\n", + "latest_file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "902d514e", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus sort datastream files based on datetime\n", + "files = os.listdir(datastream_dir)\n", + "file_sorted = files.copy()\n", + "file_sorted.sort() \n", + "print(file_sorted[:5])\n", + "\n", + "# to reverse\n", + "file_sorted_reverse = files.copy() \n", + "file_sorted_reverse.sort(reverse=True)\n", + "print(file_sorted_reverse[:5])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec5923b2", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: pattern matching\n", + "# filter the 200709** files under datastream_dir\n", + "files_filter = glob.glob(f'{datastream_dir}/*.200709*.*')\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c4a4aa18", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: use armnotebook_utils.file_filter (TODO) to filter files based on datastream info \n" + ] + }, + { + "cell_type": "markdown", + "id": "cfeb9efc", + "metadata": {}, + "source": [ + "### Load data\n", + "The arm data is stored in [NetCDF](https://en.wikipedia.org/wiki/NetCDF#:~:text=%22NetCDF%20(network%20Common%20Data%20Form,format%20for%20representing%20scientific%20data.) format. We can use xarray's `open_dataset` method to load single file, or `open_mfdataset` to open multiple files. (Note: the latter will still return a single xarray dataset object by combining multiple files.)\n", + "\n", + "Note: open_dataset keeps the file handle open and lazy loads its contents. All parameters are passed directly to open_dataset. It is a preferable method over load_dataset for memory-effiency. (more details, please see [xarray.open_dataset](https://docs.xarray.dev/en/stable/generated/xarray.open_dataset.html))\n", + "\n", + "See the following example in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a440a329", + "metadata": {}, + "outputs": [], + "source": [ + "# open single file\n", + "full_path = latest_file\n", + "print(full_path)\n", + "ds_single = xr.open_dataset(full_path)\n", + "ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0143a3d", + "metadata": {}, + "outputs": [], + "source": [ + "print(ds_single)\n", + "print(type(ds_single))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c0f8939", + "metadata": {}, + "outputs": [], + "source": [ + "# open multiple files\n", + "n_files = 3\n", + "full_paths = [os.path.join(datastream_dir, f_path) for f_path in file_sorted_reverse[:n_files]]\n", + "print(full_paths)\n", + "try: # Note: sometimes multiple files cannot be merged, so we used try except here.\n", + " ds_mutiple = xr.open_mfdataset(full_paths)\n", + " # ds_mutiple\n", + " print(type(ds_mutiple))\n", + " print(ds_mutiple)\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "3fa59943", + "metadata": {}, + "source": [ + "## Explore the data" + ] + }, + { + "cell_type": "markdown", + "id": "571f69b9", + "metadata": {}, + "source": [ + "### NetCDF Data structure \n", + "\n", + "\n", + "(If you are confident with NetCDF basics and xarray essentials, feel free to skip this session.)\n", + "Before we dive into data exploration, there are some eseential concpets we should be familiar with (shown below): \n", + "* Dataset\n", + "* Data array\n", + "* Variable\n", + "* Dimenssion\n", + "* Coordinate\n", + "* Data Type\n", + "* Meta Data (Attributes)\n", + "\n", + "We will not go into details about NetCDF basics and here are some references you might find helpful\n", + "* [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#)\n", + "* [Network Common Data Form (NetCDF)](https://www.unidata.ucar.edu/software/netcdf/) and [A Brief History of (netCDF) Time](https://www.unidata.ucar.edu/software/netcdf/time/recs.html)\n", + "\n", + "\n", + "\n", + "### Xarray essentials\n", + "Earlier we introduced the xarray pacakge and used `open_dataset` and `open_mfdataset` to retrieve NetCDF data file as an xarray Dataset object. Recall that you can review the data in a notebook by using `print(ds)` or `ds`. \n", + "\n", + "Using xarray to retrieve the aforementioned NetCDF basics is straightforwared. In general,\n", + "* Dataset: `ds`\n", + "* Data array: `ds.variables`\n", + "* Variable: `ds.ds.variables`\n", + "* Dimenssion: `ds.dims`\n", + "* Coordinate: `ds.coords`\n", + "* Data Type: `type`\n", + "* Meta Data (Attributes)\n", + "\n", + "Also, here are some references if you are new to xarray\n", + "* [Xarray in 45 minutes](https://tutorial.xarray.dev/overview/xarray-in-45-min.html)\n", + "* [Handling NetCDF Files using XArray for Absolute Beginners](https://towardsdatascience.com/handling-netcdf-files-using-xarray-for-absolute-beginners-111a8ab4463f)\n", + "\n", + "Try the following commands in action\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b3eac323", + "metadata": {}, + "outputs": [], + "source": [ + "ds = ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77ecf85d", + "metadata": {}, + "outputs": [], + "source": [ + "# Dataset \n", + "ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25e7de09", + "metadata": {}, + "outputs": [], + "source": [ + "# Data array, Variable\n", + "# Note: the info can be overwhelming. Do not attempt to grasp everything at the first glance.\n", + "ds.variables" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c41b67e", + "metadata": {}, + "outputs": [], + "source": [ + "# dimenssions\n", + "ds.dims" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "156f1dfc", + "metadata": {}, + "outputs": [], + "source": [ + "# coordinates\n", + "ds.coords" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "277d6064", + "metadata": {}, + "outputs": [], + "source": [ + "# Meta Data (Attributes)\n", + "ds.attrs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d334681f", + "metadata": {}, + "outputs": [], + "source": [ + "# type\n", + "print(type(ds))\n", + "print(type(ds.variables))\n", + "print(type(ds.dims))\n", + "print(type(ds.coords))\n", + "print(type(ds.attrs))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "643399d6", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: retrieve variable names only\n", + "list(ds.variables)" + ] + }, + { + "cell_type": "markdown", + "id": "1cf36878", + "metadata": {}, + "source": [ + "#### Discussion: variable vs. coordinates vs. dimenssions. \n", + "From [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#):\n", + "\n", + "* Dimession: A dimension may be used to represent a real physical dimension, for example, time, latitude, longitude, or height. A dimension might also be used to index other quantities, for example station or model-run-number.\n", + "A netCDF dimension has both a name and a length. A dimension length is an arbitrary positive integer, except that one dimension in a netCDF dataset can have the length UNLIMITED.\n", + "\n", + "* Variables: Variables are used to store the bulk of the data in a netCDF dataset. A variable represents an array of values of the same type. A scalar value is treated as a 0-dimensional array. A variable has a name, a data type, and a shape described by its list of dimensions specified when the variable is created. A variable may also have associated attributes, which may be added, deleted or changed after the variable is created.\n", + "\n", + "* Coordinate (Variables): It is legal for a variable to have the same name as a dimension. Such variables have no special meaning to the netCDF library. However there is a convention that such variables should be treated in a special way by software using this library.\n", + "A variable with the same name as a dimension is called a coordinate variable. It typically defines a physical coordinate corresponding to that dimension.\n", + "\n", + "\n", + "Tips: By definition, coordinates and dimenssions are also variable. For example ds.time is a coordinate but it is also a special variable. But in practice, when we talk about variable, it implies regular/non-coordinate variable. We will use this convention for the remaining notebook. " + ] + }, + { + "cell_type": "markdown", + "id": "d47120f9", + "metadata": {}, + "source": [ + "### Xarray Variable\n", + "\n", + "\n", + "We can use `ds.variables` to access varialbes of a dataset. But many times viewing print-out the whole datasets can be overwhemling. Instead, we would work on the individual variable (also called Xarray data array.) using the `ds[var_name]` syntax, where `var_name` is the name of the variable. See the following examples in action.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5a57e136", + "metadata": {}, + "outputs": [], + "source": [ + "print(type(ds[\"time\"]))\n", + "ds[\"time\"]" + ] + }, + { + "cell_type": "markdown", + "id": "51ec643e", + "metadata": {}, + "source": [ + "#### Variable properties\n", + "For individual varible, the following properties are mostly used with the assocaited xarray method to retrieve them. (assuming var = ds[var_name])\n", + "* name: `var.name`\n", + "* data content: `var.data`\n", + "* attributes: `var.attrs`\n", + "* dimenstions: `var.dims`\n", + "* data type: `var.data.dtype`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a632a525", + "metadata": {}, + "outputs": [], + "source": [ + "var_name = \"time\"\n", + "var = ds[var_name]\n", + "\n", + "print(\"var.name: \\n\", var.name, \"\\n\")\n", + "print(\"var.data: \\n\", var.data, \"\\n\")\n", + "print(\"var.attrs: \\n\", var.attrs, \"\\n\")\n", + "print(\"var.dims: \\n\", var.dims, \"\\n\")\n", + "print(\"var.data.dtype: \\n\", var.data.dtype, \"\\n\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b256b07f", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: indivdual variable info table (collect and display variable info as a tabular format)\n", + "df_info = pd.DataFrame()\n", + "df_info[\"var_name\"] = list(ds.variables)\n", + "df_info[\"dims\"] = df_info.var_name.apply(lambda x: ds[x].dims)\n", + "df_info[\"is_dim\"] = df_info.var_name.apply(lambda x: x in ds.dims)\n", + "df_info[\"n_dim\"] = df_info.var_name.apply(lambda x: len(ds[x].dims))\n", + "df_info[\"attrs\"] = df_info.var_name.apply(lambda x: ds[x].attrs)\n", + "df_info[\"dtype\"] = df_info.var_name.apply(lambda x: ds[x].data.dtype)\n", + "df_info" + ] + }, + { + "cell_type": "markdown", + "id": "e6a36b96", + "metadata": {}, + "source": [ + "### Data cleaning/Preprocessing (skipped)\n", + "Data cleaning and preprocessing is an important stage in the the data analysis pipeline. However it will be out of the scope of this notebook. For more information about data cleaning and preprocessing basics, here are some references. \n", + "* [Xarray Fundamentals](https://earth-env-data-science.github.io/lectures/xarray/xarray.html)\n", + "* [Xarray Tutorial — Pangeo Gallery documentation](http://gallery.pangeo.io/repos/pangeo-data/pangeo-tutorial-gallery/xarray.html)\n", + "* [Pythonic Data Cleaning With pandas and NumPy](https://realpython.com/python-data-cleaning-numpy-pandas/)\n", + "* [Pandas - Cleaning Data](https://www.w3schools.com/python/pandas/pandas_cleaning.asp)" + ] + }, + { + "cell_type": "markdown", + "id": "ae6e1a69", + "metadata": {}, + "source": [ + "### Plotting\n", + "Data visualization (or plotting) is another important data analysis topic and deserves its own discussion. Here in this notebook we will only demonstrate basic tool for simple data visualization tasks.\n", + "\n", + "Here are some reference you might find useful:\n", + "* xarray plotting: https://docs.xarray.dev/en/stable/user-guide/plotting.html\n", + "* Atmospheric Community Toolkit (ACT): https://arm-doe.github.io/ACT/index.html\n", + "\n", + "Note: this notebook is auto-generated using a template. It uses a general idea to select variable(s) to plot and is not customized to each indivdual notebook. Feel free to change the variables in intrrests, especially certain figure is failed to plot." + ] + }, + { + "cell_type": "markdown", + "id": "7e417fb2", + "metadata": {}, + "source": [ + "#### 1-dimenssional basic time series plot\n", + "\n", + "For the following plot we would like to find variables such that\n", + "* it has one and only one dimession\n", + "* \"time\" is its coordinate variable\n", + "* it is not a dimenssion itself,\n", + "* it is not a special variable with substrings within [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "\n", + "Please see the following example in action. (For more details about pandas filtering, please see the following references.)\n", + "* [pandas: multiple conditions while indexing data frame](https://stackoverflow.com/questions/22591174/pandas-multiple-conditions-while-indexing-data-frame-unexpected-behavior)\n", + "* [pandas.DataFrame.apply](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.apply.html)\n", + "* [How to test if a string contains one of the substrings in a list...](https://stackoverflow.com/questions/26577516/how-to-test-if-a-string-contains-one-of-the-substrings-in-a-list-in-pandas" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30edac2d", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==1, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter = df_info[(df_info.n_dim == 1) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4777a659", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_1d = df_filter.var_name.values[0]\n", + " var_1d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb5ae985", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " ds[var_1d].plot()\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "9e41066e", + "metadata": {}, + "source": [ + "#### 2-dimenssional basic plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09bd4adc", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==2, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_2 = df_info[(df_info.n_dim == 2) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2cdea1a9", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_2d = df_filter_2.var_name.values[0]\n", + " var_2d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5f48879", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " print(ds[var_2d].dims)\n", + " # ds[var_2d].plot()\n", + "\n", + " # conventionally, use \"time\" as x-axis\n", + " ds[var_2d].plot(x=\"time\")\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "7554b2a6", + "metadata": {}, + "source": [ + "#### qc-plotting (optional)\n", + "\n", + "Note: act qc plotting has more strict requirement, one of them is the associated qc variable need to have \"flag_masks\" attributes. Which is added by using ds = act.io.armfiles.read_netcdf(files_list), then ds.clean.cleanup()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71c2096f", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " ds_act = act.io.armfiles.read_netcdf(full_path)\n", + " print(type(ds_act))\n", + " ds_act.clean.cleanup()\n", + "\n", + " # or \n", + " # ds.clean.cleanup()\n", + "except Exception as e:\n", + " print(\"ERROR\", e)\n", + " ds_act = ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "844f8505", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter valid variables for ACT qc plotting\n", + "condition = (\"qc_\" + df_info.var_name).apply(lambda x: ds_act[x].attrs.get(\"flag_masks\") is not None \n", + " if x in list(ds.variables) else False)\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_3 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings))) &\n", + " condition\n", + " ]\n", + "df_filter_3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd38cc6", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "try:\n", + " qc_variable = df_filter_3.var_name.values[0]\n", + " print(qc_variable)\n", + "except Exception as e:\n", + " print(e)\n", + " \n", + "try:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + " qc_ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "d76e4d27", + "metadata": {}, + "source": [ + "#### bonus: choose variables to plot from a dropdown menu " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb733804", + "metadata": {}, + "outputs": [], + "source": [ + "# Valid variables filtering\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_4 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82d5c20d", + "metadata": {}, + "outputs": [], + "source": [ + "# example 1: using xarray plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2fc9aaa4", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# fig, ax = plt.subplots(figsize=(10, 4))\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "# fig.clear() # Remove old lines from plot and plot new one\n", + "# if len(ds[var].dims)==2:\n", + "# ds[var].plot(x=\"time\", add_colorbar=False)\n", + "# else:\n", + "# ds[var].plot()\n", + "# plt.grid()\n", + "# plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fabdd802", + "metadata": {}, + "outputs": [], + "source": [ + "# example 2: using act plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "962b4186", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "\n", + "\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "\n", + "# i_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + "# i_display.add_subplots((1,), figsize=(10, 4))\n", + "# ax = i_display.plot(var, subplot_index=(0,), set_title=f\"{var} ({ds_act[var].attrs['long_name']})\",)\n", + "\n", + "# ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "# ax.grid()\n", + "# plt.show()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/SHALLOWCUMULUS/shallowcumulus.c1.ipynb b/VAPs/quicklook/SHALLOWCUMULUS/shallowcumulus.c1.ipynb new file mode 100644 index 00000000..e060d9dc --- /dev/null +++ b/VAPs/quicklook/SHALLOWCUMULUS/shallowcumulus.c1.ipynb @@ -0,0 +1,297 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# SHALLOWCUMULUS.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/shallowcumulus) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'shallowcumulus'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2022-07-29', 'facility': 'C1', 'site': 'sgp', 'start_date': '2000-07-02'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2022-07-27'\n", + "date_end = '2022-07-29'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['flag_shcu', 'flag_st_shcu', 'flag_shcu_st']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'flag_shcu'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/SHALLOWCUMULUS/shcusummary.c1.ipynb b/VAPs/quicklook/SHALLOWCUMULUS/shcusummary.c1.ipynb new file mode 100644 index 00000000..f22c2770 --- /dev/null +++ b/VAPs/quicklook/SHALLOWCUMULUS/shcusummary.c1.ipynb @@ -0,0 +1,297 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# SHCUSUMMARY.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/shallowcumulus) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'shcusummary'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2023-05-29', 'facility': 'C1', 'site': 'sgp', 'start_date': '2000-07-01'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2023-05-26'\n", + "date_end = '2023-05-28'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['shallowcumulus_event']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'shallowcumulus_event'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/SO2-AIR/SO2-AIR_tutorial.ipynb b/VAPs/quicklook/SO2-AIR/SO2-AIR_tutorial.ipynb new file mode 100644 index 00000000..7f0d5340 --- /dev/null +++ b/VAPs/quicklook/SO2-AIR/SO2-AIR_tutorial.ipynb @@ -0,0 +1,838 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# AAFSO2.C1 Notebook\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/so2-air) for more information about this vap." + ] + }, + { + "cell_type": "markdown", + "id": "97097763", + "metadata": {}, + "source": [ + "In this notebook, we demonstrate the workflow to explore ARM vap data (using aafso2 as an example.) Value-added products (VAPs) are higher-order data products that have been analyzed and processed to ease scientists’ use of ARM data in atmospheric research and global climate models. \n", + "Here is the main content we will cover." + ] + }, + { + "cell_type": "markdown", + "id": "eddec40f", + "metadata": {}, + "source": [ + "# Table of Content\n", + "## Access the data\n", + "* How to retrieve the data\n", + "* Data path and file name conventions\n", + "* Load data\n", + "## Explore the data\n", + "* NetCDF Data structure\n", + "* Xarray essentials\n", + "* Xarray Variable\n", + "## Plot the data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "import random\n", + "\n", + "import glob\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "2372d5be", + "metadata": {}, + "source": [ + "## Access the data" + ] + }, + { + "cell_type": "markdown", + "id": "5b0d9684", + "metadata": {}, + "source": [ + "### How to retrieve the data\n", + "We assume the path \"/data/archive\" is available where you are running this notebook. You can use `os.path.exists(\"/data/archive\")` to verify if the path exists at your machine.\n", + "\n", + "\n", + "### Data path and file name conventions\n", + "There several common terminologies regarding ARM data, for example, data-stream-name, data-level, site, facility, instrument, etc. (For more details, please see [ARM Data File Standards Version 1.2](https://www.google.com/search?q=arm+datastream+facility+cite+definition&rlz=1C1GCEJ_enUS1029US1029&ei=hb41ZICrBPukqtsPvMODIA&ved=0ahUKEwjAgeiS0aL-AhV7kmoFHbzhAAQQ4dUDCBA&uact=5&oq=arm+datastream+facility+cite+definition&gs_lcp=Cgxnd3Mtd2l6LXNlcnAQAzIFCAAQogQyBQgAEKIEMgUIABCiBDIFCAAQogQyBQgAEKIEOgoIABBHENYEELADOgoIIRCgARDDBBAKSgQIQRgAUJ4DWIoOYO0PaAFwAXgAgAGMAYgBuQmSAQMyLjmYAQCgAQHIAQjAAQE&sclient=gws-wiz-serp).) \n", + "\n", + "For example, this notebook is called `aafso2.c1`, where `aafso2` is the \"datastream name\", and the `{process.ds_class_level}}` is the \"data level\".\n", + "\n", + "This datastream also contains site `cor` and facility `F1`. (Note: individual datastream might have multiple site-facility pairs.)\n", + "In such a case, the data of this data-stream is stored at `/data/archive/cor/coraafso2F1.c1`, which is in the format of `//.`. We can use the following method to assign the data-stream directory `datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )`\n", + "\n", + "The data files under datastream_dir also follows naming conventions. But once reach the datastream_dir level, the most import file naming convention to differentiate the files is \"yyyyMMdd.hhmmss\", which comes handy to filter out files based on datetime. For example, we can use `glob.glob(f'{datastream_dir}/*.200709*.*')` to filter files in 2007 September.\n", + "\n", + "(Note: refer to the https://adc.arm.gov/discovery/#/ and https://adc.arm.gov/solr8/metadata/select API to explore ARM datastream and assoicated available sites and facilities\n", + "\n", + "Please see the following examples in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d7e9eb85", + "metadata": {}, + "outputs": [], + "source": [ + "# Verify if DATA_DIR path exists\n", + "DATA_DIR = \"/data/archive\"\n", + "os.path.exists(DATA_DIR)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "586993fd", + "metadata": {}, + "outputs": [], + "source": [ + "# Speicify datastream_dir following the path conventions and check its existence\n", + "DATASTREAM_NAME = \"aafso2\"\n", + "DATA_LEVEL = \"c1\"\n", + "site = \"cor\"\n", + "facility = \"F1\"\n", + "datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )\n", + "print(datastream_dir)\n", + "print(os.path.exists(datastream_dir))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0742f7c1", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: list 5 (random) files under datastream_dir\n", + "files = os.listdir(datastream_dir)\n", + "files[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39b98a36", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: get most recent file\n", + "list_of_files = glob.glob(f\"{datastream_dir}/*\") # * means all if need specific format then *.csv\n", + "latest_file = max(list_of_files, key=os.path.getctime)\n", + "latest_file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "902d514e", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus sort datastream files based on datetime\n", + "files = os.listdir(datastream_dir)\n", + "file_sorted = files.copy()\n", + "file_sorted.sort() \n", + "print(file_sorted[:5])\n", + "\n", + "# to reverse\n", + "file_sorted_reverse = files.copy() \n", + "file_sorted_reverse.sort(reverse=True)\n", + "print(file_sorted_reverse[:5])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec5923b2", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: pattern matching\n", + "# filter the 200709** files under datastream_dir\n", + "files_filter = glob.glob(f'{datastream_dir}/*.200709*.*')\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c4a4aa18", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: use armnotebook_utils.file_filter (TODO) to filter files based on datastream info \n" + ] + }, + { + "cell_type": "markdown", + "id": "cfeb9efc", + "metadata": {}, + "source": [ + "### Load data\n", + "The arm data is stored in [NetCDF](https://en.wikipedia.org/wiki/NetCDF#:~:text=%22NetCDF%20(network%20Common%20Data%20Form,format%20for%20representing%20scientific%20data.) format. We can use xarray's `open_dataset` method to load single file, or `open_mfdataset` to open multiple files. (Note: the latter will still return a single xarray dataset object by combining multiple files.)\n", + "\n", + "Note: open_dataset keeps the file handle open and lazy loads its contents. All parameters are passed directly to open_dataset. It is a preferable method over load_dataset for memory-effiency. (more details, please see [xarray.open_dataset](https://docs.xarray.dev/en/stable/generated/xarray.open_dataset.html))\n", + "\n", + "See the following example in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a440a329", + "metadata": {}, + "outputs": [], + "source": [ + "# open single file\n", + "full_path = latest_file\n", + "print(full_path)\n", + "ds_single = xr.open_dataset(full_path)\n", + "ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0143a3d", + "metadata": {}, + "outputs": [], + "source": [ + "print(ds_single)\n", + "print(type(ds_single))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c0f8939", + "metadata": {}, + "outputs": [], + "source": [ + "# open multiple files\n", + "n_files = 3\n", + "full_paths = [os.path.join(datastream_dir, f_path) for f_path in file_sorted_reverse[:n_files]]\n", + "print(full_paths)\n", + "try: # Note: sometimes multiple files cannot be merged, so we used try except here.\n", + " ds_mutiple = xr.open_mfdataset(full_paths)\n", + " # ds_mutiple\n", + " print(type(ds_mutiple))\n", + " print(ds_mutiple)\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "3fa59943", + "metadata": {}, + "source": [ + "## Explore the data" + ] + }, + { + "cell_type": "markdown", + "id": "571f69b9", + "metadata": {}, + "source": [ + "### NetCDF Data structure \n", + "\n", + "\n", + "(If you are confident with NetCDF basics and xarray essentials, feel free to skip this session.)\n", + "Before we dive into data exploration, there are some eseential concpets we should be familiar with (shown below): \n", + "* Dataset\n", + "* Data array\n", + "* Variable\n", + "* Dimenssion\n", + "* Coordinate\n", + "* Data Type\n", + "* Meta Data (Attributes)\n", + "\n", + "We will not go into details about NetCDF basics and here are some references you might find helpful\n", + "* [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#)\n", + "* [Network Common Data Form (NetCDF)](https://www.unidata.ucar.edu/software/netcdf/) and [A Brief History of (netCDF) Time](https://www.unidata.ucar.edu/software/netcdf/time/recs.html)\n", + "\n", + "\n", + "\n", + "### Xarray essentials\n", + "Earlier we introduced the xarray pacakge and used `open_dataset` and `open_mfdataset` to retrieve NetCDF data file as an xarray Dataset object. Recall that you can review the data in a notebook by using `print(ds)` or `ds`. \n", + "\n", + "Using xarray to retrieve the aforementioned NetCDF basics is straightforwared. In general,\n", + "* Dataset: `ds`\n", + "* Data array: `ds.variables`\n", + "* Variable: `ds.ds.variables`\n", + "* Dimenssion: `ds.dims`\n", + "* Coordinate: `ds.coords`\n", + "* Data Type: `type`\n", + "* Meta Data (Attributes)\n", + "\n", + "Also, here are some references if you are new to xarray\n", + "* [Xarray in 45 minutes](https://tutorial.xarray.dev/overview/xarray-in-45-min.html)\n", + "* [Handling NetCDF Files using XArray for Absolute Beginners](https://towardsdatascience.com/handling-netcdf-files-using-xarray-for-absolute-beginners-111a8ab4463f)\n", + "\n", + "Try the following commands in action\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b3eac323", + "metadata": {}, + "outputs": [], + "source": [ + "ds = ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77ecf85d", + "metadata": {}, + "outputs": [], + "source": [ + "# Dataset \n", + "ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25e7de09", + "metadata": {}, + "outputs": [], + "source": [ + "# Data array, Variable\n", + "# Note: the info can be overwhelming. Do not attempt to grasp everything at the first glance.\n", + "ds.variables" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c41b67e", + "metadata": {}, + "outputs": [], + "source": [ + "# dimenssions\n", + "ds.dims" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "156f1dfc", + "metadata": {}, + "outputs": [], + "source": [ + "# coordinates\n", + "ds.coords" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "277d6064", + "metadata": {}, + "outputs": [], + "source": [ + "# Meta Data (Attributes)\n", + "ds.attrs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d334681f", + "metadata": {}, + "outputs": [], + "source": [ + "# type\n", + "print(type(ds))\n", + "print(type(ds.variables))\n", + "print(type(ds.dims))\n", + "print(type(ds.coords))\n", + "print(type(ds.attrs))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "643399d6", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: retrieve variable names only\n", + "list(ds.variables)" + ] + }, + { + "cell_type": "markdown", + "id": "1cf36878", + "metadata": {}, + "source": [ + "#### Discussion: variable vs. coordinates vs. dimenssions. \n", + "From [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#):\n", + "\n", + "* Dimession: A dimension may be used to represent a real physical dimension, for example, time, latitude, longitude, or height. A dimension might also be used to index other quantities, for example station or model-run-number.\n", + "A netCDF dimension has both a name and a length. A dimension length is an arbitrary positive integer, except that one dimension in a netCDF dataset can have the length UNLIMITED.\n", + "\n", + "* Variables: Variables are used to store the bulk of the data in a netCDF dataset. A variable represents an array of values of the same type. A scalar value is treated as a 0-dimensional array. A variable has a name, a data type, and a shape described by its list of dimensions specified when the variable is created. A variable may also have associated attributes, which may be added, deleted or changed after the variable is created.\n", + "\n", + "* Coordinate (Variables): It is legal for a variable to have the same name as a dimension. Such variables have no special meaning to the netCDF library. However there is a convention that such variables should be treated in a special way by software using this library.\n", + "A variable with the same name as a dimension is called a coordinate variable. It typically defines a physical coordinate corresponding to that dimension.\n", + "\n", + "\n", + "Tips: By definition, coordinates and dimenssions are also variable. For example ds.time is a coordinate but it is also a special variable. But in practice, when we talk about variable, it implies regular/non-coordinate variable. We will use this convention for the remaining notebook. " + ] + }, + { + "cell_type": "markdown", + "id": "d47120f9", + "metadata": {}, + "source": [ + "### Xarray Variable\n", + "\n", + "\n", + "We can use `ds.variables` to access varialbes of a dataset. But many times viewing print-out the whole datasets can be overwhemling. Instead, we would work on the individual variable (also called Xarray data array.) using the `ds[var_name]` syntax, where `var_name` is the name of the variable. See the following examples in action.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5a57e136", + "metadata": {}, + "outputs": [], + "source": [ + "print(type(ds[\"time\"]))\n", + "ds[\"time\"]" + ] + }, + { + "cell_type": "markdown", + "id": "51ec643e", + "metadata": {}, + "source": [ + "#### Variable properties\n", + "For individual varible, the following properties are mostly used with the assocaited xarray method to retrieve them. (assuming var = ds[var_name])\n", + "* name: `var.name`\n", + "* data content: `var.data`\n", + "* attributes: `var.attrs`\n", + "* dimenstions: `var.dims`\n", + "* data type: `var.data.dtype`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a632a525", + "metadata": {}, + "outputs": [], + "source": [ + "var_name = \"time\"\n", + "var = ds[var_name]\n", + "\n", + "print(\"var.name: \\n\", var.name, \"\\n\")\n", + "print(\"var.data: \\n\", var.data, \"\\n\")\n", + "print(\"var.attrs: \\n\", var.attrs, \"\\n\")\n", + "print(\"var.dims: \\n\", var.dims, \"\\n\")\n", + "print(\"var.data.dtype: \\n\", var.data.dtype, \"\\n\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b256b07f", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: indivdual variable info table (collect and display variable info as a tabular format)\n", + "df_info = pd.DataFrame()\n", + "df_info[\"var_name\"] = list(ds.variables)\n", + "df_info[\"dims\"] = df_info.var_name.apply(lambda x: ds[x].dims)\n", + "df_info[\"is_dim\"] = df_info.var_name.apply(lambda x: x in ds.dims)\n", + "df_info[\"n_dim\"] = df_info.var_name.apply(lambda x: len(ds[x].dims))\n", + "df_info[\"attrs\"] = df_info.var_name.apply(lambda x: ds[x].attrs)\n", + "df_info[\"dtype\"] = df_info.var_name.apply(lambda x: ds[x].data.dtype)\n", + "df_info" + ] + }, + { + "cell_type": "markdown", + "id": "e6a36b96", + "metadata": {}, + "source": [ + "### Data cleaning/Preprocessing (skipped)\n", + "Data cleaning and preprocessing is an important stage in the the data analysis pipeline. However it will be out of the scope of this notebook. For more information about data cleaning and preprocessing basics, here are some references. \n", + "* [Xarray Fundamentals](https://earth-env-data-science.github.io/lectures/xarray/xarray.html)\n", + "* [Xarray Tutorial — Pangeo Gallery documentation](http://gallery.pangeo.io/repos/pangeo-data/pangeo-tutorial-gallery/xarray.html)\n", + "* [Pythonic Data Cleaning With pandas and NumPy](https://realpython.com/python-data-cleaning-numpy-pandas/)\n", + "* [Pandas - Cleaning Data](https://www.w3schools.com/python/pandas/pandas_cleaning.asp)" + ] + }, + { + "cell_type": "markdown", + "id": "ae6e1a69", + "metadata": {}, + "source": [ + "### Plotting\n", + "Data visualization (or plotting) is another important data analysis topic and deserves its own discussion. Here in this notebook we will only demonstrate basic tool for simple data visualization tasks.\n", + "\n", + "Here are some reference you might find useful:\n", + "* xarray plotting: https://docs.xarray.dev/en/stable/user-guide/plotting.html\n", + "* Atmospheric Community Toolkit (ACT): https://arm-doe.github.io/ACT/index.html\n", + "\n", + "Note: this notebook is auto-generated using a template. It uses a general idea to select variable(s) to plot and is not customized to each indivdual notebook. Feel free to change the variables in intrrests, especially certain figure is failed to plot." + ] + }, + { + "cell_type": "markdown", + "id": "7e417fb2", + "metadata": {}, + "source": [ + "#### 1-dimenssional basic time series plot\n", + "\n", + "For the following plot we would like to find variables such that\n", + "* it has one and only one dimession\n", + "* \"time\" is its coordinate variable\n", + "* it is not a dimenssion itself,\n", + "* it is not a special variable with substrings within [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "\n", + "Please see the following example in action. (For more details about pandas filtering, please see the following references.)\n", + "* [pandas: multiple conditions while indexing data frame](https://stackoverflow.com/questions/22591174/pandas-multiple-conditions-while-indexing-data-frame-unexpected-behavior)\n", + "* [pandas.DataFrame.apply](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.apply.html)\n", + "* [How to test if a string contains one of the substrings in a list...](https://stackoverflow.com/questions/26577516/how-to-test-if-a-string-contains-one-of-the-substrings-in-a-list-in-pandas" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30edac2d", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==1, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter = df_info[(df_info.n_dim == 1) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4777a659", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_1d = df_filter.var_name.values[0]\n", + " var_1d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb5ae985", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " ds[var_1d].plot()\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "9e41066e", + "metadata": {}, + "source": [ + "#### 2-dimenssional basic plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09bd4adc", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==2, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_2 = df_info[(df_info.n_dim == 2) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2cdea1a9", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_2d = df_filter_2.var_name.values[0]\n", + " var_2d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5f48879", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " print(ds[var_2d].dims)\n", + " # ds[var_2d].plot()\n", + "\n", + " # conventionally, use \"time\" as x-axis\n", + " ds[var_2d].plot(x=\"time\")\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "7554b2a6", + "metadata": {}, + "source": [ + "#### qc-plotting (optional)\n", + "\n", + "Note: act qc plotting has more strict requirement, one of them is the associated qc variable need to have \"flag_masks\" attributes. Which is added by using ds = act.io.armfiles.read_netcdf(files_list), then ds.clean.cleanup()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71c2096f", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " ds_act = act.io.armfiles.read_netcdf(full_path)\n", + " print(type(ds_act))\n", + " ds_act.clean.cleanup()\n", + "\n", + " # or \n", + " # ds.clean.cleanup()\n", + "except Exception as e:\n", + " print(\"ERROR\", e)\n", + " ds_act = ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "844f8505", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter valid variables for ACT qc plotting\n", + "condition = (\"qc_\" + df_info.var_name).apply(lambda x: ds_act[x].attrs.get(\"flag_masks\") is not None \n", + " if x in list(ds.variables) else False)\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_3 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings))) &\n", + " condition\n", + " ]\n", + "df_filter_3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd38cc6", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "try:\n", + " qc_variable = df_filter_3.var_name.values[0]\n", + " print(qc_variable)\n", + "except Exception as e:\n", + " print(e)\n", + " \n", + "try:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + " qc_ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "d76e4d27", + "metadata": {}, + "source": [ + "#### bonus: choose variables to plot from a dropdown menu " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb733804", + "metadata": {}, + "outputs": [], + "source": [ + "# Valid variables filtering\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_4 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82d5c20d", + "metadata": {}, + "outputs": [], + "source": [ + "# example 1: using xarray plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2fc9aaa4", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# fig, ax = plt.subplots(figsize=(10, 4))\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "# fig.clear() # Remove old lines from plot and plot new one\n", + "# if len(ds[var].dims)==2:\n", + "# ds[var].plot(x=\"time\", add_colorbar=False)\n", + "# else:\n", + "# ds[var].plot()\n", + "# plt.grid()\n", + "# plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fabdd802", + "metadata": {}, + "outputs": [], + "source": [ + "# example 2: using act plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "962b4186", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "\n", + "\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "\n", + "# i_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + "# i_display.add_subplots((1,), figsize=(10, 4))\n", + "# ax = i_display.plot(var, subplot_index=(0,), set_title=f\"{var} ({ds_act[var].attrs['long_name']})\",)\n", + "\n", + "# ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "# ax.grid()\n", + "# plt.show()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/SO2-AIR/aafso2.c1.ipynb b/VAPs/quicklook/SO2-AIR/aafso2.c1.ipynb new file mode 100644 index 00000000..64bb0b4c --- /dev/null +++ b/VAPs/quicklook/SO2-AIR/aafso2.c1.ipynb @@ -0,0 +1,297 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# AAFSO2.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/so2-air) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'aafso2'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2018-12-08', 'facility': 'F1', 'site': 'cor', 'start_date': '2018-11-04'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'cor', 'F1' )\n", + "\n", + "date_start = '2018-12-06'\n", + "date_end = '2018-12-08'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['so2']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'so2'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/SONDEADJUST/.ipynb_checkpoints/SONDEADJUST_tutorial-checkpoint.ipynb b/VAPs/quicklook/SONDEADJUST/.ipynb_checkpoints/SONDEADJUST_tutorial-checkpoint.ipynb new file mode 100644 index 00000000..698be192 --- /dev/null +++ b/VAPs/quicklook/SONDEADJUST/.ipynb_checkpoints/SONDEADJUST_tutorial-checkpoint.ipynb @@ -0,0 +1,4058 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# SONDEADJUST.C1 Notebook\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/sondeadjust) for more information about this vap." + ] + }, + { + "cell_type": "markdown", + "id": "97097763", + "metadata": {}, + "source": [ + "In this notebook, we demonstrate the workflow to explore ARM vap data (using sondeadjust as an example.) Value-added products (VAPs) are higher-order data products that have been analyzed and processed to ease scientists’ use of ARM data in atmospheric research and global climate models. \n", + "Here is the main content we will cover." + ] + }, + { + "cell_type": "markdown", + "id": "eddec40f", + "metadata": {}, + "source": [ + "# Table of Content\n", + "## Access the data\n", + "* How to retrieve the data\n", + "* Data path and file name conventions\n", + "* Load data\n", + "## Explore the data\n", + "* NetCDF Data structure\n", + "* Xarray essentials\n", + "* Xarray Variable\n", + "## Plot the data" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "import random\n", + "\n", + "import glob\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "2372d5be", + "metadata": {}, + "source": [ + "## Access the data" + ] + }, + { + "cell_type": "markdown", + "id": "5b0d9684", + "metadata": {}, + "source": [ + "### How to retrieve the data\n", + "We assume the path \"/data/archive\" is available where you are running this notebook. You can use `os.path.exists(\"/data/archive\")` to verify if the path exists at your machine.\n", + "\n", + "\n", + "### Data path and file name conventions\n", + "There several common terminologies regarding ARM data, for example, data-stream-name, data-level, site, facility, instrument, etc. (For more details, please see [ARM Data File Standards Version 1.2](https://www.google.com/search?q=arm+datastream+facility+cite+definition&rlz=1C1GCEJ_enUS1029US1029&ei=hb41ZICrBPukqtsPvMODIA&ved=0ahUKEwjAgeiS0aL-AhV7kmoFHbzhAAQQ4dUDCBA&uact=5&oq=arm+datastream+facility+cite+definition&gs_lcp=Cgxnd3Mtd2l6LXNlcnAQAzIFCAAQogQyBQgAEKIEMgUIABCiBDIFCAAQogQyBQgAEKIEOgoIABBHENYEELADOgoIIRCgARDDBBAKSgQIQRgAUJ4DWIoOYO0PaAFwAXgAgAGMAYgBuQmSAQMyLjmYAQCgAQHIAQjAAQE&sclient=gws-wiz-serp).) \n", + "\n", + "For example, this notebook is called `sondeadjust.c1`, where `sondeadjust` is the \"datastream name\", and the `{process.ds_class_level}}` is the \"data level\".\n", + "\n", + "This datastream also contains site `nsa` and facility `C1`. (Note: individual datastream might have multiple site-facility pairs.)\n", + "In such a case, the data of this data-stream is stored at `/data/archive/nsa/nsasondeadjustC1.c1`, which is in the format of `//.`. We can use the following method to assign the data-stream directory `datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )`\n", + "\n", + "The data files under datastream_dir also follows naming conventions. But once reach the datastream_dir level, the most import file naming convention to differentiate the files is \"yyyyMMdd.hhmmss\", which comes handy to filter out files based on datetime. For example, we can use `glob.glob(f'{datastream_dir}/*.200709*.*')` to filter files in 2007 September.\n", + "\n", + "(Note: refer to the https://adc.arm.gov/discovery/#/ and https://adc.arm.gov/solr8/metadata/select API to explore ARM datastream and assoicated available sites and facilities\n", + "\n", + "Please see the following examples in action" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "d7e9eb85", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Verify if DATA_DIR path exists\n", + "DATA_DIR = \"/data/archive\"\n", + "os.path.exists(DATA_DIR)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "586993fd", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "/data/archive/nsa/nsasondeadjustC1.c1\n", + "True\n" + ] + } + ], + "source": [ + "# Speicify datastream_dir following the path conventions and check its existence\n", + "DATASTREAM_NAME = \"sondeadjust\"\n", + "DATA_LEVEL = \"c1\"\n", + "site = \"nsa\"\n", + "facility = \"C1\"\n", + "datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )\n", + "print(datastream_dir)\n", + "print(os.path.exists(datastream_dir))\n" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "0742f7c1", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['nsasondeadjustC1.c1.20110710.054600.cdf',\n", + " 'nsasondeadjustC1.c1.20090319.051600.cdf',\n", + " 'nsasondeadjustC1.c1.20080508.052600.cdf',\n", + " 'nsasondeadjustC1.c1.20070816.181100.cdf',\n", + " 'nsasondeadjustC1.c1.20110923.174200.cdf']" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# bonus: list 5 (random) files under datastream_dir\n", + "files = os.listdir(datastream_dir)\n", + "files[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "39b98a36", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20080601.053500.cdf'" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# bonus: get most recent file\n", + "list_of_files = glob.glob(f\"{datastream_dir}/*\") # * means all if need specific format then *.csv\n", + "latest_file = max(list_of_files, key=os.path.getctime)\n", + "latest_file" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "902d514e", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "['nsasondeadjustC1.c1.20020428.184800.cdf', 'nsasondeadjustC1.c1.20020428.220500.cdf', 'nsasondeadjustC1.c1.20020428.235900.cdf', 'nsasondeadjustC1.c1.20020429.013100.cdf', 'nsasondeadjustC1.c1.20020429.182500.cdf']\n", + "['nsasondeadjustC1.c1.20120716.173000.cdf', 'nsasondeadjustC1.c1.20120716.053000.cdf', 'nsasondeadjustC1.c1.20120715.214900.cdf', 'nsasondeadjustC1.c1.20120715.173000.cdf', 'nsasondeadjustC1.c1.20120715.053000.cdf']\n" + ] + } + ], + "source": [ + "# bonus sort datastream files based on datetime\n", + "files = os.listdir(datastream_dir)\n", + "file_sorted = files.copy()\n", + "file_sorted.sort() \n", + "print(file_sorted[:5])\n", + "\n", + "# to reverse\n", + "file_sorted_reverse = files.copy() \n", + "file_sorted_reverse.sort(reverse=True)\n", + "print(file_sorted_reverse[:5])" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "ec5923b2", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070921.173300.cdf',\n", + " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070929.190700.cdf',\n", + " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070925.201400.cdf',\n", + " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070928.174800.cdf',\n", + " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070930.172800.cdf',\n", + " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070905.052400.cdf',\n", + " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070927.052700.cdf',\n", + " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070920.165900.cdf',\n", + " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070929.053300.cdf',\n", + " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070909.053200.cdf',\n", + " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070904.204700.cdf',\n", + " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070913.210100.cdf',\n", + " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070925.052400.cdf',\n", + " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070907.173500.cdf',\n", + " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070919.053400.cdf',\n", + " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070901.172500.cdf',\n", + " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070919.174900.cdf',\n", + " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070903.210800.cdf',\n", + " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070913.180600.cdf',\n", + " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070914.204200.cdf',\n", + " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070916.052500.cdf',\n", + " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070917.213100.cdf',\n", + " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070904.052100.cdf',\n", + " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070915.172400.cdf',\n", + " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070923.172500.cdf',\n", + " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070903.173300.cdf',\n", + " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070928.205300.cdf',\n", + " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070905.174000.cdf',\n", + " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070926.213000.cdf',\n", + " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070918.052700.cdf',\n", + " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070914.173800.cdf',\n", + " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070914.053000.cdf',\n", + " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070918.205800.cdf',\n", + " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070911.052500.cdf',\n", + " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070917.180300.cdf',\n", + " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070904.174000.cdf',\n", + " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070901.052400.cdf',\n", + " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070918.181600.cdf',\n", + " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070911.174500.cdf',\n", + " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070907.053800.cdf',\n", + " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070910.213600.cdf',\n", + " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070902.172900.cdf',\n", + " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070924.173400.cdf',\n", + " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070910.052900.cdf',\n", + " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070906.220400.cdf',\n", + " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070920.053400.cdf',\n", + " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070926.173600.cdf',\n", + " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070906.053200.cdf',\n", + " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070923.052600.cdf',\n", + " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070911.221800.cdf',\n", + " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070917.052500.cdf',\n", + " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070927.180200.cdf',\n", + " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070907.213000.cdf',\n", + " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070910.173600.cdf',\n", + " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070912.212300.cdf',\n", + " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070912.052600.cdf',\n", + " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070922.172900.cdf',\n", + " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070925.180300.cdf',\n", + " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070909.172700.cdf',\n", + " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070921.052700.cdf',\n", + " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070928.053300.cdf',\n", + " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070903.052400.cdf',\n", + " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070902.052300.cdf',\n", + " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070930.052600.cdf',\n", + " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070912.173700.cdf',\n", + " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070915.052900.cdf',\n", + " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070927.211200.cdf',\n", + " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070916.173200.cdf',\n", + " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070913.053500.cdf',\n", + " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070922.061100.cdf',\n", + " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070908.052500.cdf',\n", + " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070905.210700.cdf',\n", + " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070906.175700.cdf',\n", + " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070908.175400.cdf',\n", + " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070924.052900.cdf',\n", + " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070922.053900.cdf']" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# bonus: pattern matching\n", + "# filter the 200709** files under datastream_dir\n", + "files_filter = glob.glob(f'{datastream_dir}/*.200709*.*')\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "c4a4aa18", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: use armnotebook_utils.file_filter (TODO) to filter files based on datastream info \n" + ] + }, + { + "cell_type": "markdown", + "id": "cfeb9efc", + "metadata": {}, + "source": [ + "### Load data\n", + "The arm data is stored in [NetCDF](https://en.wikipedia.org/wiki/NetCDF#:~:text=%22NetCDF%20(network%20Common%20Data%20Form,format%20for%20representing%20scientific%20data.) format. We can use xarray's `open_dataset` method to load single file, or `open_mfdataset` to open multiple files. (Note: the latter will still return a single xarray dataset object by combining multiple files.)\n", + "\n", + "Note: open_dataset keeps the file handle open and lazy loads its contents. All parameters are passed directly to open_dataset. It is a preferable method over load_dataset for memory-effiency. (more details, please see [xarray.open_dataset](https://docs.xarray.dev/en/stable/generated/xarray.open_dataset.html))\n", + "\n", + "See the following example in action" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "a440a329", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20080601.053500.cdf\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
<xarray.Dataset>\n",
+       "Dimensions:       (time: 2975)\n",
+       "Coordinates:\n",
+       "  * time          (time) datetime64[ns] 2008-06-01T05:35:00 ... 2008-06-01T07...\n",
+       "Data variables: (12/35)\n",
+       "    base_time     datetime64[ns] ...\n",
+       "    time_offset   (time) datetime64[ns] ...\n",
+       "    qc_time       (time) int32 ...\n",
+       "    pres          (time) float32 ...\n",
+       "    qc_pres       (time) int32 ...\n",
+       "    tdry          (time) float32 ...\n",
+       "    ...            ...\n",
+       "    qc_rh_scaled  (time) int32 ...\n",
+       "    dp_scaled     (time) float32 ...\n",
+       "    qc_dp_scaled  (time) int32 ...\n",
+       "    lat           (time) float32 ...\n",
+       "    lon           (time) float32 ...\n",
+       "    alt           (time) float32 ...\n",
+       "Attributes: (12/16)\n",
+       "    process_version:                $State: vap-sonde_adjust-8.0-0.sol5_10$\n",
+       "    command_line:                   sonde_adjust -d 20080601 -f nsaC1 -a 0\n",
+       "    site_id:                        nsa\n",
+       "    facility_id:                    C1: Barrow, Alaska\n",
+       "    reference1:                     Wang et.al. 2002. "Corrections of Humidit...\n",
+       "    reference2:                     Miloshevich et.al. 2004. "Development and...\n",
+       "    ...                             ...\n",
+       "    station_elevation:              8 m MSL\n",
+       "    input_datastreams_description:  A string consisting of the datastream(s),...\n",
+       "    input_datastreams_num:          6\n",
+       "    input_datastreams:              nsasondewnpnC1.b1 : 5.190000 : 20080601.0...\n",
+       "    zeb_platform:                   nsasondeadjustC1.c1\n",
+       "    history:                        created by user gervais on machine emeral...
" + ], + "text/plain": [ + "\n", + "Dimensions: (time: 2975)\n", + "Coordinates:\n", + " * time (time) datetime64[ns] 2008-06-01T05:35:00 ... 2008-06-01T07...\n", + "Data variables: (12/35)\n", + " base_time datetime64[ns] ...\n", + " time_offset (time) datetime64[ns] ...\n", + " qc_time (time) int32 ...\n", + " pres (time) float32 ...\n", + " qc_pres (time) int32 ...\n", + " tdry (time) float32 ...\n", + " ... ...\n", + " qc_rh_scaled (time) int32 ...\n", + " dp_scaled (time) float32 ...\n", + " qc_dp_scaled (time) int32 ...\n", + " lat (time) float32 ...\n", + " lon (time) float32 ...\n", + " alt (time) float32 ...\n", + "Attributes: (12/16)\n", + " process_version: $State: vap-sonde_adjust-8.0-0.sol5_10$\n", + " command_line: sonde_adjust -d 20080601 -f nsaC1 -a 0\n", + " site_id: nsa\n", + " facility_id: C1: Barrow, Alaska\n", + " reference1: Wang et.al. 2002. \"Corrections of Humidit...\n", + " reference2: Miloshevich et.al. 2004. \"Development and...\n", + " ... ...\n", + " station_elevation: 8 m MSL\n", + " input_datastreams_description: A string consisting of the datastream(s),...\n", + " input_datastreams_num: 6\n", + " input_datastreams: nsasondewnpnC1.b1 : 5.190000 : 20080601.0...\n", + " zeb_platform: nsasondeadjustC1.c1\n", + " history: created by user gervais on machine emeral..." + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# open single file\n", + "full_path = latest_file\n", + "print(full_path)\n", + "ds_single = xr.open_dataset(full_path)\n", + "ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "b0143a3d", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Dimensions: (time: 2975)\n", + "Coordinates:\n", + " * time (time) datetime64[ns] 2008-06-01T05:35:00 ... 2008-06-01T07...\n", + "Data variables: (12/35)\n", + " base_time datetime64[ns] ...\n", + " time_offset (time) datetime64[ns] ...\n", + " qc_time (time) int32 ...\n", + " pres (time) float32 ...\n", + " qc_pres (time) int32 ...\n", + " tdry (time) float32 ...\n", + " ... ...\n", + " qc_rh_scaled (time) int32 ...\n", + " dp_scaled (time) float32 ...\n", + " qc_dp_scaled (time) int32 ...\n", + " lat (time) float32 ...\n", + " lon (time) float32 ...\n", + " alt (time) float32 ...\n", + "Attributes: (12/16)\n", + " process_version: $State: vap-sonde_adjust-8.0-0.sol5_10$\n", + " command_line: sonde_adjust -d 20080601 -f nsaC1 -a 0\n", + " site_id: nsa\n", + " facility_id: C1: Barrow, Alaska\n", + " reference1: Wang et.al. 2002. \"Corrections of Humidit...\n", + " reference2: Miloshevich et.al. 2004. \"Development and...\n", + " ... ...\n", + " station_elevation: 8 m MSL\n", + " input_datastreams_description: A string consisting of the datastream(s),...\n", + " input_datastreams_num: 6\n", + " input_datastreams: nsasondewnpnC1.b1 : 5.190000 : 20080601.0...\n", + " zeb_platform: nsasondeadjustC1.c1\n", + " history: created by user gervais on machine emeral...\n", + "\n" + ] + } + ], + "source": [ + "print(ds_single)\n", + "print(type(ds_single))" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "1c0f8939", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "['/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20120716.173000.cdf', '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20120716.053000.cdf', '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20120715.214900.cdf']\n", + "\n", + "\n", + "Dimensions: (time: 7872)\n", + "Coordinates:\n", + " * time (time) datetime64[ns] 2012-07-15T21:49:00 ... 2012-07-16T18...\n", + "Data variables: (12/35)\n", + " base_time (time) datetime64[ns] 2012-07-15T21:49:00 ... 2012-07-16T17...\n", + " time_offset (time) datetime64[ns] dask.array\n", + " qc_time (time) int32 dask.array\n", + " pres (time) float32 dask.array\n", + " qc_pres (time) int32 dask.array\n", + " tdry (time) float32 dask.array\n", + " ... ...\n", + " qc_rh_scaled (time) int32 dask.array\n", + " dp_scaled (time) float32 dask.array\n", + " qc_dp_scaled (time) int32 dask.array\n", + " lat (time) float32 dask.array\n", + " lon (time) float32 dask.array\n", + " alt (time) float32 dask.array\n", + "Attributes: (12/16)\n", + " process_version: $State: vap-sonde_adjust-8.0-0.sol5_10$\n", + " command_line: sonde_adjust -d 20120715 -f nsaC1 -a 0\n", + " site_id: nsa\n", + " facility_id: C1: Barrow, Alaska\n", + " reference1: Wang et.al. 2002. \"Corrections of Humidit...\n", + " reference2: Miloshevich et.al. 2004. \"Development and...\n", + " ... ...\n", + " station_elevation: 8 m MSL\n", + " input_datastreams_description: A string consisting of the datastream(s),...\n", + " input_datastreams_num: 6\n", + " input_datastreams: nsasondewnpnC1.b1 : 10.800000 : 20120715....\n", + " zeb_platform: nsasondeadjustC1.c1\n", + " history: created by user gervais on machine emeral...\n" + ] + } + ], + "source": [ + "# open multiple files\n", + "n_files = 3\n", + "full_paths = [os.path.join(datastream_dir, f_path) for f_path in file_sorted_reverse[:n_files]]\n", + "print(full_paths)\n", + "try: # Note: sometimes multiple files cannot be merged, so we used try except here.\n", + " ds_mutiple = xr.open_mfdataset(full_paths)\n", + " # ds_mutiple\n", + " print(type(ds_mutiple))\n", + " print(ds_mutiple)\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "3fa59943", + "metadata": {}, + "source": [ + "## Explore the data" + ] + }, + { + "cell_type": "markdown", + "id": "571f69b9", + "metadata": {}, + "source": [ + "### NetCDF Data structure \n", + "\n", + "\n", + "(If you are confident with NetCDF basics and xarray essentials, feel free to skip this session.)\n", + "Before we dive into data exploration, there are some eseential concpets we should be familiar with (shown below): \n", + "* Dataset\n", + "* Data array\n", + "* Variable\n", + "* Dimenssion\n", + "* Coordinate\n", + "* Data Type\n", + "* Meta Data (Attributes)\n", + "\n", + "We will not go into details about NetCDF basics and here are some references you might find helpful\n", + "* [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#)\n", + "* [Network Common Data Form (NetCDF)](https://www.unidata.ucar.edu/software/netcdf/) and [A Brief History of (netCDF) Time](https://www.unidata.ucar.edu/software/netcdf/time/recs.html)\n", + "\n", + "\n", + "\n", + "### Xarray essentials\n", + "Earlier we introduced the xarray pacakge and used `open_dataset` and `open_mfdataset` to retrieve NetCDF data file as an xarray Dataset object. Recall that you can review the data in a notebook by using `print(ds)` or `ds`. \n", + "\n", + "Using xarray to retrieve the aforementioned NetCDF basics is straightforwared. In general,\n", + "* Dataset: `ds`\n", + "* Data array: `ds.variables`\n", + "* Variable: `ds.ds.variables`\n", + "* Dimenssion: `ds.dims`\n", + "* Coordinate: `ds.coords`\n", + "* Data Type: `type`\n", + "* Meta Data (Attributes)\n", + "\n", + "Also, here are some references if you are new to xarray\n", + "* [Xarray in 45 minutes](https://tutorial.xarray.dev/overview/xarray-in-45-min.html)\n", + "* [Handling NetCDF Files using XArray for Absolute Beginners](https://towardsdatascience.com/handling-netcdf-files-using-xarray-for-absolute-beginners-111a8ab4463f)\n", + "\n", + "Try the following commands in action\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "b3eac323", + "metadata": {}, + "outputs": [], + "source": [ + "ds = ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "77ecf85d", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
<xarray.Dataset>\n",
+       "Dimensions:       (time: 2975)\n",
+       "Coordinates:\n",
+       "  * time          (time) datetime64[ns] 2008-06-01T05:35:00 ... 2008-06-01T07...\n",
+       "Data variables: (12/35)\n",
+       "    base_time     datetime64[ns] ...\n",
+       "    time_offset   (time) datetime64[ns] ...\n",
+       "    qc_time       (time) int32 ...\n",
+       "    pres          (time) float32 ...\n",
+       "    qc_pres       (time) int32 ...\n",
+       "    tdry          (time) float32 ...\n",
+       "    ...            ...\n",
+       "    qc_rh_scaled  (time) int32 ...\n",
+       "    dp_scaled     (time) float32 ...\n",
+       "    qc_dp_scaled  (time) int32 ...\n",
+       "    lat           (time) float32 ...\n",
+       "    lon           (time) float32 ...\n",
+       "    alt           (time) float32 ...\n",
+       "Attributes: (12/16)\n",
+       "    process_version:                $State: vap-sonde_adjust-8.0-0.sol5_10$\n",
+       "    command_line:                   sonde_adjust -d 20080601 -f nsaC1 -a 0\n",
+       "    site_id:                        nsa\n",
+       "    facility_id:                    C1: Barrow, Alaska\n",
+       "    reference1:                     Wang et.al. 2002. "Corrections of Humidit...\n",
+       "    reference2:                     Miloshevich et.al. 2004. "Development and...\n",
+       "    ...                             ...\n",
+       "    station_elevation:              8 m MSL\n",
+       "    input_datastreams_description:  A string consisting of the datastream(s),...\n",
+       "    input_datastreams_num:          6\n",
+       "    input_datastreams:              nsasondewnpnC1.b1 : 5.190000 : 20080601.0...\n",
+       "    zeb_platform:                   nsasondeadjustC1.c1\n",
+       "    history:                        created by user gervais on machine emeral...
" + ], + "text/plain": [ + "\n", + "Dimensions: (time: 2975)\n", + "Coordinates:\n", + " * time (time) datetime64[ns] 2008-06-01T05:35:00 ... 2008-06-01T07...\n", + "Data variables: (12/35)\n", + " base_time datetime64[ns] ...\n", + " time_offset (time) datetime64[ns] ...\n", + " qc_time (time) int32 ...\n", + " pres (time) float32 ...\n", + " qc_pres (time) int32 ...\n", + " tdry (time) float32 ...\n", + " ... ...\n", + " qc_rh_scaled (time) int32 ...\n", + " dp_scaled (time) float32 ...\n", + " qc_dp_scaled (time) int32 ...\n", + " lat (time) float32 ...\n", + " lon (time) float32 ...\n", + " alt (time) float32 ...\n", + "Attributes: (12/16)\n", + " process_version: $State: vap-sonde_adjust-8.0-0.sol5_10$\n", + " command_line: sonde_adjust -d 20080601 -f nsaC1 -a 0\n", + " site_id: nsa\n", + " facility_id: C1: Barrow, Alaska\n", + " reference1: Wang et.al. 2002. \"Corrections of Humidit...\n", + " reference2: Miloshevich et.al. 2004. \"Development and...\n", + " ... ...\n", + " station_elevation: 8 m MSL\n", + " input_datastreams_description: A string consisting of the datastream(s),...\n", + " input_datastreams_num: 6\n", + " input_datastreams: nsasondewnpnC1.b1 : 5.190000 : 20080601.0...\n", + " zeb_platform: nsasondeadjustC1.c1\n", + " history: created by user gervais on machine emeral..." + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Dataset \n", + "ds" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "25e7de09", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Frozen({'base_time': \n", + "[1 values with dtype=datetime64[ns]]\n", + "Attributes:\n", + " string: 1-Jun-2008,5:35:00 GMT\n", + " long_name: Base time in Epoch, 'time_offset': \n", + "[2975 values with dtype=datetime64[ns]]\n", + "Attributes:\n", + " long_name: Time offset from base_time, 'time': \n", + "array(['2008-06-01T05:35:00.000000000', '2008-06-01T05:35:02.000000000',\n", + " '2008-06-01T05:35:04.000000000', ..., '2008-06-01T07:14:04.000000000',\n", + " '2008-06-01T07:14:06.000000000', '2008-06-01T07:14:08.000000000'],\n", + " dtype='datetime64[ns]')\n", + "Attributes:\n", + " long_name: Time offset from midnight, 'qc_time': \n", + "[2975 values with dtype=int32]\n", + "Attributes: (12/13)\n", + " long_name: Quality check results on field: Time offset from mi...\n", + " units: unitless\n", + " description: This field contains bit packed values which should ...\n", + " bit_1_description: Delta time between current and previous samples is ...\n", + " bit_1_assessment: Bad\n", + " bit_2_description: Delta time between current and previous samples is ...\n", + " ... ...\n", + " bit_3_description: Delta time between current and previous samples is ...\n", + " bit_3_assessment: Bad\n", + " delta_t_lower_limit: 20.0\n", + " delta_t_upper_limit: 20.0\n", + " prior_sample_flag: 1\n", + " comment: If the 'prior_sample_flag' is set the first sample ..., 'pres': \n", + "[2975 values with dtype=float32]\n", + "Attributes:\n", + " long_name: Barometric pressure\n", + " units: hPa\n", + " valid_min: 0.0\n", + " valid_max: 1100.0, 'qc_pres': \n", + "[2975 values with dtype=int32]\n", + "Attributes:\n", + " long_name: Quality check results on field: Barometric pressure\n", + " units: unitless\n", + " description: This field contains bit packed values which should be...\n", + " bit_1_description: Value is less than the valid_min.\n", + " bit_1_assessment: Indeterminate\n", + " bit_2_description: Value is greater than the valid_max.\n", + " bit_2_assessment: Indeterminate\n", + " bit_3_description: Data value not available in input file, data value se...\n", + " bit_3_assessment: Bad, 'tdry': \n", + "[2975 values with dtype=float32]\n", + "Attributes:\n", + " long_name: Dry bulb temperature\n", + " units: C\n", + " valid_min: -80.0\n", + " valid_max: 50.0, 'qc_tdry': \n", + "[2975 values with dtype=int32]\n", + "Attributes:\n", + " long_name: Quality check results on field: Dry bulb temperature\n", + " units: unitless\n", + " description: This field contains bit packed values which should be...\n", + " bit_1_description: Value is less than the valid_min.\n", + " bit_1_assessment: Indeterminate\n", + " bit_2_description: Value is greater than the valid_max.\n", + " bit_2_assessment: Indeterminate\n", + " bit_3_description: Data value not available in input file, data value se...\n", + " bit_3_assessment: Bad, 'dp': \n", + "[2975 values with dtype=float32]\n", + "Attributes:\n", + " long_name: Dewpoint temperature\n", + " units: C\n", + " valid_min: -110.0\n", + " valid_max: 50.0, 'qc_dp': \n", + "[2975 values with dtype=int32]\n", + "Attributes:\n", + " long_name: Quality check results on field: Dewpoint temperature\n", + " units: unitless\n", + " description: This field contains bit packed values which should be...\n", + " bit_1_description: Value is less than the valid_min.\n", + " bit_1_assessment: Indeterminate\n", + " bit_2_description: Value is greater than the valid_max.\n", + " bit_2_assessment: Indeterminate\n", + " bit_3_description: Data value not available in input file, data value se...\n", + " bit_3_assessment: Bad, 'wspd': \n", + "[2975 values with dtype=float32]\n", + "Attributes:\n", + " long_name: Wind speed\n", + " units: m/s\n", + " valid_min: 0.0\n", + " valid_max: 100.0, 'qc_wspd': \n", + "[2975 values with dtype=int32]\n", + "Attributes:\n", + " long_name: Quality check results on field: Wind speed\n", + " units: unitless\n", + " description: This field contains bit packed values which should be...\n", + " bit_1_description: Value is less than the valid_min.\n", + " bit_1_assessment: Indeterminate\n", + " bit_2_description: Value is greater than the valid_max.\n", + " bit_2_assessment: Indeterminate\n", + " bit_3_description: Data value not available in input file, data value se...\n", + " bit_3_assessment: Bad, 'deg': \n", + "[2975 values with dtype=float32]\n", + "Attributes:\n", + " long_name: Wind direction\n", + " units: deg\n", + " valid_min: 0.0\n", + " valid_max: 360.0, 'qc_deg': \n", + "[2975 values with dtype=int32]\n", + "Attributes:\n", + " long_name: Quality check results on field: Wind direction\n", + " units: unitless\n", + " description: This field contains bit packed values which should be...\n", + " bit_1_description: Value is less than the valid_min.\n", + " bit_1_assessment: Indeterminate\n", + " bit_2_description: Value is greater than the valid_max.\n", + " bit_2_assessment: Indeterminate\n", + " bit_3_description: Data value not available in input file, data value se...\n", + " bit_3_assessment: Bad, 'rh': \n", + "[2975 values with dtype=float32]\n", + "Attributes:\n", + " long_name: Relative humidity\n", + " units: %\n", + " valid_min: 0.0\n", + " valid_max: 105.0, 'qc_rh': \n", + "[2975 values with dtype=int32]\n", + "Attributes:\n", + " long_name: Quality check results on field: Relative humidity\n", + " units: unitless\n", + " description: This field contains bit packed values which should be...\n", + " bit_1_description: Value is less than the valid_min.\n", + " bit_1_assessment: Indeterminate\n", + " bit_2_description: Value is greater than the valid_max.\n", + " bit_2_assessment: Indeterminate\n", + " bit_3_description: Data value not available in input file, data value se...\n", + " bit_3_assessment: Bad, 'u_wind': \n", + "[2975 values with dtype=float32]\n", + "Attributes:\n", + " long_name: Eastward wind component\n", + " units: m/s\n", + " calc: -1 * sin( deg ) * wspd\n", + " valid_min: -75.0\n", + " valid_max: 75.0, 'qc_u_wind': \n", + "[2975 values with dtype=int32]\n", + "Attributes:\n", + " long_name: Quality check results on field: Eastward wind component\n", + " units: unitless\n", + " description: This field contains bit packed values which should be...\n", + " bit_1_description: Value is less than the valid_min.\n", + " bit_1_assessment: Indeterminate\n", + " bit_2_description: Value is greater than the valid_max.\n", + " bit_2_assessment: Indeterminate\n", + " bit_3_description: Data value not available in input file, data value se...\n", + " bit_3_assessment: Bad, 'v_wind': \n", + "[2975 values with dtype=float32]\n", + "Attributes:\n", + " long_name: Northward wind component\n", + " units: m/s\n", + " calc: -1 * cos( deg ) * wspd\n", + " valid_min: -75.0\n", + " valid_max: 75.0, 'qc_v_wind': \n", + "[2975 values with dtype=int32]\n", + "Attributes:\n", + " long_name: Quality check results on field: Northward wind component\n", + " units: unitless\n", + " description: This field contains bit packed values which should be...\n", + " bit_1_description: Value is less than the valid_min.\n", + " bit_1_assessment: Indeterminate\n", + " bit_2_description: Value is greater than the valid_max.\n", + " bit_2_assessment: Indeterminate\n", + " bit_3_description: Data value not available in input file, data value se...\n", + " bit_3_assessment: Bad, 'wstat': \n", + "[2975 values with dtype=float32]\n", + "Attributes:\n", + " long_name: Wind status\n", + " units: unitless, 'asc': \n", + "[2975 values with dtype=float32]\n", + "Attributes:\n", + " long_name: Ascent rate\n", + " units: m/s\n", + " valid_min: -10.0\n", + " valid_max: 20.0, 'qc_asc': \n", + "[2975 values with dtype=int32]\n", + "Attributes:\n", + " long_name: Quality check results on field: Ascent rate\n", + " units: unitless\n", + " description: This field contains bit packed values which should be...\n", + " bit_1_description: Value is less than the valid_min.\n", + " bit_1_assessment: Indeterminate\n", + " bit_2_description: Value is greater than the valid_max.\n", + " bit_2_assessment: Indeterminate\n", + " bit_3_description: Data value not available in input file, data value se...\n", + " bit_3_assessment: Bad, 'rh_smooth': \n", + "[2975 values with dtype=float32]\n", + "Attributes:\n", + " long_name: Smoothed original relative humidity\n", + " units: %\n", + " valid_min: 0.0\n", + " valid_max: 100.0\n", + " note: Intermediate RH profile created by smoothing original RH sond..., 'qc_rh_smooth': \n", + "[2975 values with dtype=int32]\n", + "Attributes:\n", + " long_name: Quality check results on field: Smoothed original rel...\n", + " units: unitless\n", + " description: This field contains bit packed values which should be...\n", + " bit_1_description: Value is less than the valid_min.\n", + " bit_1_assessment: Indeterminate\n", + " bit_2_description: Value is greater than the valid_max.\n", + " bit_2_assessment: Indeterminate\n", + " bit_3_description: Data value not available in input file, data value se...\n", + " bit_3_assessment: Bad, 'rh_biased': \n", + "[2975 values with dtype=float32]\n", + "Attributes:\n", + " long_name: Dry bias corrected relative humidity\n", + " units: %\n", + " valid_min: 0.0\n", + " valid_max: 100.0\n", + " note1: Eliminates the dry bias as described in Wang 2002\n", + " note2: This field differs from the rh_smooth field for only the RS-8..., 'qc_rh_biased': \n", + "[2975 values with dtype=int32]\n", + "Attributes:\n", + " long_name: Quality check results on field: Dry bias corrected re...\n", + " units: unitless\n", + " description: This field contains bit packed values which should be...\n", + " bit_1_description: Value is less than the valid_min.\n", + " bit_1_assessment: Indeterminate\n", + " bit_2_description: Value is greater than the valid_max.\n", + " bit_2_assessment: Indeterminate\n", + " bit_3_description: Data value not available in input file, data value se...\n", + " bit_3_assessment: Bad, 'rh_adjust': \n", + "[2975 values with dtype=float32]\n", + "Attributes:\n", + " long_name: Final corrected ambient relative humidity\n", + " units: %\n", + " valid_min: 0.0\n", + " valid_max: 100.0\n", + " note: corrects for sensor time-lag (RS-80 sondes) and the solar war..., 'qc_rh_adjust': \n", + "[2975 values with dtype=int32]\n", + "Attributes:\n", + " long_name: Quality check results on field: Final corrected ambie...\n", + " units: unitless\n", + " description: This field contains bit packed values which should be...\n", + " bit_1_description: Value is less than the valid_min.\n", + " bit_1_assessment: Indeterminate\n", + " bit_2_description: Value is greater than the valid_max.\n", + " bit_2_assessment: Indeterminate\n", + " bit_3_description: Data value not available in input file, data value se...\n", + " bit_3_assessment: Bad, 'rh_scaled': \n", + "[2975 values with dtype=float32]\n", + "Attributes:\n", + " long_name: Scaled final corrected ambient relative humidity\n", + " units: %\n", + " valid_min: 0.0\n", + " valid_max: 100.0\n", + " note1: scale factor is the be_pwv from mwrret1liljclou datasteam\n", + " note2: when there is no mwr or when pwv < 0.8, values are -9999, 'qc_rh_scaled': \n", + "[2975 values with dtype=int32]\n", + "Attributes:\n", + " long_name: Quality check results on field: Scaled final correcte...\n", + " units: unitless\n", + " description: This field contains bit packed values which should be...\n", + " bit_1_description: Value is less than the valid_min.\n", + " bit_1_assessment: Indeterminate\n", + " bit_2_description: Value is greater than the valid_max.\n", + " bit_2_assessment: Indeterminate\n", + " bit_3_description: Data value not available in input file, data value se...\n", + " bit_3_assessment: Bad\n", + " bit_4_description: The value of the pwv from the mwr file used to scale ...\n", + " bit_4_assessment: Bad, 'dp_scaled': \n", + "[2975 values with dtype=float32]\n", + "Attributes:\n", + " long_name: Scaled dewpoint temperature\n", + " units: C\n", + " valid_min: -110.0\n", + " valid_max: 50.0\n", + " note1: scale factor is the be_pwv from mwrret1liljclou datastream\n", + " note2: when there is no mwr or when pwv < 0.8, values are -9999, 'qc_dp_scaled': \n", + "[2975 values with dtype=int32]\n", + "Attributes:\n", + " long_name: Quality check results on field: Scaled dewpoint tempe...\n", + " units: unitless\n", + " description: This field contains bit packed values which should be...\n", + " bit_1_description: Value is less than the valid_min.\n", + " bit_1_assessment: Indeterminate\n", + " bit_2_description: Value is greater than the valid_max.\n", + " bit_2_assessment: Indeterminate\n", + " bit_3_description: Data value not available in input file, data value se...\n", + " bit_3_assessment: Bad\n", + " bit_4_description: The value of the pwv from the mwr file used to scale ...\n", + " bit_4_assessment: Bad, 'lat': \n", + "[2975 values with dtype=float32]\n", + "Attributes:\n", + " long_name: North latitude\n", + " units: degree_N\n", + " valid_min: -90.0\n", + " valid_max: 90.0, 'lon': \n", + "[2975 values with dtype=float32]\n", + "Attributes:\n", + " long_name: East longitude\n", + " units: degree_E\n", + " valid_min: -180.0\n", + " valid_max: 180.0, 'alt': \n", + "[2975 values with dtype=float32]\n", + "Attributes:\n", + " long_name: Altitude above mean sea level\n", + " units: m})" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Data array, Variable\n", + "# Note: the info can be overwhelming. Do not attempt to grasp everything at the first glance.\n", + "ds.variables" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "8c41b67e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Frozen({'time': 2975})" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# dimenssions\n", + "ds.dims" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "156f1dfc", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Coordinates:\n", + " * time (time) datetime64[ns] 2008-06-01T05:35:00 ... 2008-06-01T07:14:08" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# coordinates\n", + "ds.coords" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "277d6064", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'process_version': '$State: vap-sonde_adjust-8.0-0.sol5_10$',\n", + " 'command_line': 'sonde_adjust -d 20080601 -f nsaC1 -a 0',\n", + " 'site_id': 'nsa',\n", + " 'facility_id': 'C1: Barrow, Alaska',\n", + " 'reference1': 'Wang et.al. 2002. \"Corrections of Humidity Measurement Errors from the Vaisala RS80-Radiosonde -- Application to TOGA COARE Data.\" Journal of Atmospheric and Oceanic Technology',\n", + " 'reference2': 'Miloshevich et.al. 2004. \"Development and Validation of a Time-Lag Correction for Vaisala Radiosonde Humidity Measurement.\" Journal of Atmospheric and Oceanic Technology',\n", + " 'reference3': 'Miloshevich et.al. 2009. \"Accuracy Assessment and Correction of Vaisala RS92 Radiosonde Water Vapor Measurements.\" Journal of Geophysical Research--Atmospheres',\n", + " 'qc_standards_version': '1.0',\n", + " 'dod_version': '5.0',\n", + " 'sonde_serial_number': 'C3526394',\n", + " 'station_elevation': '8 m MSL',\n", + " 'input_datastreams_description': 'A string consisting of the datastream(s), datastream version(s), and datastream date (range).',\n", + " 'input_datastreams_num': 6,\n", + " 'input_datastreams': 'nsasondewnpnC1.b1 : 5.190000 : 20080601.053500-20080601.175900 ;\\nnsametC1.b1 : 4.100000 : 20080531.234500-20080602.000000 ;',\n", + " 'zeb_platform': 'nsasondeadjustC1.c1',\n", + " 'history': 'created by user gervais on machine emerald at 2-Sep-2014,22:07:00, using $State: zebra-zeblib-4.23-0.el5 $'}" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Meta Data (Attributes)\n", + "ds.attrs" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "d334681f", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "\n", + "\n", + "\n", + "\n" + ] + } + ], + "source": [ + "# type\n", + "print(type(ds))\n", + "print(type(ds.variables))\n", + "print(type(ds.dims))\n", + "print(type(ds.coords))\n", + "print(type(ds.attrs))" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "643399d6", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['base_time',\n", + " 'time_offset',\n", + " 'time',\n", + " 'qc_time',\n", + " 'pres',\n", + " 'qc_pres',\n", + " 'tdry',\n", + " 'qc_tdry',\n", + " 'dp',\n", + " 'qc_dp',\n", + " 'wspd',\n", + " 'qc_wspd',\n", + " 'deg',\n", + " 'qc_deg',\n", + " 'rh',\n", + " 'qc_rh',\n", + " 'u_wind',\n", + " 'qc_u_wind',\n", + " 'v_wind',\n", + " 'qc_v_wind',\n", + " 'wstat',\n", + " 'asc',\n", + " 'qc_asc',\n", + " 'rh_smooth',\n", + " 'qc_rh_smooth',\n", + " 'rh_biased',\n", + " 'qc_rh_biased',\n", + " 'rh_adjust',\n", + " 'qc_rh_adjust',\n", + " 'rh_scaled',\n", + " 'qc_rh_scaled',\n", + " 'dp_scaled',\n", + " 'qc_dp_scaled',\n", + " 'lat',\n", + " 'lon',\n", + " 'alt']" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# bonus: retrieve variable names only\n", + "list(ds.variables)" + ] + }, + { + "cell_type": "markdown", + "id": "1cf36878", + "metadata": {}, + "source": [ + "#### Discussion: variable vs. coordinates vs. dimenssions. \n", + "From [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#):\n", + "\n", + "* Dimession: A dimension may be used to represent a real physical dimension, for example, time, latitude, longitude, or height. A dimension might also be used to index other quantities, for example station or model-run-number.\n", + "A netCDF dimension has both a name and a length. A dimension length is an arbitrary positive integer, except that one dimension in a netCDF dataset can have the length UNLIMITED.\n", + "\n", + "* Variables: Variables are used to store the bulk of the data in a netCDF dataset. A variable represents an array of values of the same type. A scalar value is treated as a 0-dimensional array. A variable has a name, a data type, and a shape described by its list of dimensions specified when the variable is created. A variable may also have associated attributes, which may be added, deleted or changed after the variable is created.\n", + "\n", + "* Coordinate (Variables): It is legal for a variable to have the same name as a dimension. Such variables have no special meaning to the netCDF library. However there is a convention that such variables should be treated in a special way by software using this library.\n", + "A variable with the same name as a dimension is called a coordinate variable. It typically defines a physical coordinate corresponding to that dimension.\n", + "\n", + "\n", + "Tips: By definition, coordinates and dimenssions are also variable. For example ds.time is a coordinate but it is also a special variable. But in practice, when we talk about variable, it implies regular/non-coordinate variable. We will use this convention for the remaining notebook. " + ] + }, + { + "cell_type": "markdown", + "id": "d47120f9", + "metadata": {}, + "source": [ + "### Xarray Variable\n", + "\n", + "\n", + "We can use `ds.variables` to access varialbes of a dataset. But many times viewing print-out the whole datasets can be overwhemling. Instead, we would work on the individual variable (also called Xarray data array.) using the `ds[var_name]` syntax, where `var_name` is the name of the variable. See the following examples in action.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "5a57e136", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
<xarray.DataArray 'time' (time: 2975)>\n",
+       "array(['2008-06-01T05:35:00.000000000', '2008-06-01T05:35:02.000000000',\n",
+       "       '2008-06-01T05:35:04.000000000', ..., '2008-06-01T07:14:04.000000000',\n",
+       "       '2008-06-01T07:14:06.000000000', '2008-06-01T07:14:08.000000000'],\n",
+       "      dtype='datetime64[ns]')\n",
+       "Coordinates:\n",
+       "  * time     (time) datetime64[ns] 2008-06-01T05:35:00 ... 2008-06-01T07:14:08\n",
+       "Attributes:\n",
+       "    long_name:  Time offset from midnight
" + ], + "text/plain": [ + "\n", + "array(['2008-06-01T05:35:00.000000000', '2008-06-01T05:35:02.000000000',\n", + " '2008-06-01T05:35:04.000000000', ..., '2008-06-01T07:14:04.000000000',\n", + " '2008-06-01T07:14:06.000000000', '2008-06-01T07:14:08.000000000'],\n", + " dtype='datetime64[ns]')\n", + "Coordinates:\n", + " * time (time) datetime64[ns] 2008-06-01T05:35:00 ... 2008-06-01T07:14:08\n", + "Attributes:\n", + " long_name: Time offset from midnight" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "print(type(ds[\"time\"]))\n", + "ds[\"time\"]" + ] + }, + { + "cell_type": "markdown", + "id": "51ec643e", + "metadata": {}, + "source": [ + "#### Variable properties\n", + "For individual varible, the following properties are mostly used with the assocaited xarray method to retrieve them. (assuming var = ds[var_name])\n", + "* name: `var.name`\n", + "* data content: `var.data`\n", + "* attributes: `var.attrs`\n", + "* dimenstions: `var.dims`\n", + "* data type: `var.data.dtype`" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "a632a525", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "var.name: \n", + " time \n", + "\n", + "var.data: \n", + " ['2008-06-01T05:35:00.000000000' '2008-06-01T05:35:02.000000000'\n", + " '2008-06-01T05:35:04.000000000' ... '2008-06-01T07:14:04.000000000'\n", + " '2008-06-01T07:14:06.000000000' '2008-06-01T07:14:08.000000000'] \n", + "\n", + "var.attrs: \n", + " {'long_name': 'Time offset from midnight'} \n", + "\n", + "var.dims: \n", + " ('time',) \n", + "\n", + "var.data.dtype: \n", + " datetime64[ns] \n", + "\n" + ] + } + ], + "source": [ + "var_name = \"time\"\n", + "var = ds[var_name]\n", + "\n", + "print(\"var.name: \\n\", var.name, \"\\n\")\n", + "print(\"var.data: \\n\", var.data, \"\\n\")\n", + "print(\"var.attrs: \\n\", var.attrs, \"\\n\")\n", + "print(\"var.dims: \\n\", var.dims, \"\\n\")\n", + "print(\"var.data.dtype: \\n\", var.data.dtype, \"\\n\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "b256b07f", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
var_namedimsis_dimn_dimattrsdtype
0base_time()False0{'string': '1-Jun-2008,5:35:00 GMT', 'long_nam...datetime64[ns]
1time_offset(time,)False1{'long_name': 'Time offset from base_time'}datetime64[ns]
2time(time,)True1{'long_name': 'Time offset from midnight'}datetime64[ns]
3qc_time(time,)False1{'long_name': 'Quality check results on field:...int32
4pres(time,)False1{'long_name': 'Barometric pressure', 'units': ...float32
5qc_pres(time,)False1{'long_name': 'Quality check results on field:...int32
6tdry(time,)False1{'long_name': 'Dry bulb temperature', 'units':...float32
7qc_tdry(time,)False1{'long_name': 'Quality check results on field:...int32
8dp(time,)False1{'long_name': 'Dewpoint temperature', 'units':...float32
9qc_dp(time,)False1{'long_name': 'Quality check results on field:...int32
10wspd(time,)False1{'long_name': 'Wind speed', 'units': 'm/s', 'v...float32
11qc_wspd(time,)False1{'long_name': 'Quality check results on field:...int32
12deg(time,)False1{'long_name': 'Wind direction', 'units': 'deg'...float32
13qc_deg(time,)False1{'long_name': 'Quality check results on field:...int32
14rh(time,)False1{'long_name': 'Relative humidity', 'units': '%...float32
15qc_rh(time,)False1{'long_name': 'Quality check results on field:...int32
16u_wind(time,)False1{'long_name': 'Eastward wind component', 'unit...float32
17qc_u_wind(time,)False1{'long_name': 'Quality check results on field:...int32
18v_wind(time,)False1{'long_name': 'Northward wind component', 'uni...float32
19qc_v_wind(time,)False1{'long_name': 'Quality check results on field:...int32
20wstat(time,)False1{'long_name': 'Wind status', 'units': 'unitless'}float32
21asc(time,)False1{'long_name': 'Ascent rate', 'units': 'm/s', '...float32
22qc_asc(time,)False1{'long_name': 'Quality check results on field:...int32
23rh_smooth(time,)False1{'long_name': 'Smoothed original relative humi...float32
24qc_rh_smooth(time,)False1{'long_name': 'Quality check results on field:...int32
25rh_biased(time,)False1{'long_name': 'Dry bias corrected relative hum...float32
26qc_rh_biased(time,)False1{'long_name': 'Quality check results on field:...int32
27rh_adjust(time,)False1{'long_name': 'Final corrected ambient relativ...float32
28qc_rh_adjust(time,)False1{'long_name': 'Quality check results on field:...int32
29rh_scaled(time,)False1{'long_name': 'Scaled final corrected ambient ...float32
30qc_rh_scaled(time,)False1{'long_name': 'Quality check results on field:...int32
31dp_scaled(time,)False1{'long_name': 'Scaled dewpoint temperature', '...float32
32qc_dp_scaled(time,)False1{'long_name': 'Quality check results on field:...int32
33lat(time,)False1{'long_name': 'North latitude', 'units': 'degr...float32
34lon(time,)False1{'long_name': 'East longitude', 'units': 'degr...float32
35alt(time,)False1{'long_name': 'Altitude above mean sea level',...float32
\n", + "
" + ], + "text/plain": [ + " var_name dims is_dim n_dim \\\n", + "0 base_time () False 0 \n", + "1 time_offset (time,) False 1 \n", + "2 time (time,) True 1 \n", + "3 qc_time (time,) False 1 \n", + "4 pres (time,) False 1 \n", + "5 qc_pres (time,) False 1 \n", + "6 tdry (time,) False 1 \n", + "7 qc_tdry (time,) False 1 \n", + "8 dp (time,) False 1 \n", + "9 qc_dp (time,) False 1 \n", + "10 wspd (time,) False 1 \n", + "11 qc_wspd (time,) False 1 \n", + "12 deg (time,) False 1 \n", + "13 qc_deg (time,) False 1 \n", + "14 rh (time,) False 1 \n", + "15 qc_rh (time,) False 1 \n", + "16 u_wind (time,) False 1 \n", + "17 qc_u_wind (time,) False 1 \n", + "18 v_wind (time,) False 1 \n", + "19 qc_v_wind (time,) False 1 \n", + "20 wstat (time,) False 1 \n", + "21 asc (time,) False 1 \n", + "22 qc_asc (time,) False 1 \n", + "23 rh_smooth (time,) False 1 \n", + "24 qc_rh_smooth (time,) False 1 \n", + "25 rh_biased (time,) False 1 \n", + "26 qc_rh_biased (time,) False 1 \n", + "27 rh_adjust (time,) False 1 \n", + "28 qc_rh_adjust (time,) False 1 \n", + "29 rh_scaled (time,) False 1 \n", + "30 qc_rh_scaled (time,) False 1 \n", + "31 dp_scaled (time,) False 1 \n", + "32 qc_dp_scaled (time,) False 1 \n", + "33 lat (time,) False 1 \n", + "34 lon (time,) False 1 \n", + "35 alt (time,) False 1 \n", + "\n", + " attrs dtype \n", + "0 {'string': '1-Jun-2008,5:35:00 GMT', 'long_nam... datetime64[ns] \n", + "1 {'long_name': 'Time offset from base_time'} datetime64[ns] \n", + "2 {'long_name': 'Time offset from midnight'} datetime64[ns] \n", + "3 {'long_name': 'Quality check results on field:... int32 \n", + "4 {'long_name': 'Barometric pressure', 'units': ... float32 \n", + "5 {'long_name': 'Quality check results on field:... int32 \n", + "6 {'long_name': 'Dry bulb temperature', 'units':... float32 \n", + "7 {'long_name': 'Quality check results on field:... int32 \n", + "8 {'long_name': 'Dewpoint temperature', 'units':... float32 \n", + "9 {'long_name': 'Quality check results on field:... int32 \n", + "10 {'long_name': 'Wind speed', 'units': 'm/s', 'v... float32 \n", + "11 {'long_name': 'Quality check results on field:... int32 \n", + "12 {'long_name': 'Wind direction', 'units': 'deg'... float32 \n", + "13 {'long_name': 'Quality check results on field:... int32 \n", + "14 {'long_name': 'Relative humidity', 'units': '%... float32 \n", + "15 {'long_name': 'Quality check results on field:... int32 \n", + "16 {'long_name': 'Eastward wind component', 'unit... float32 \n", + "17 {'long_name': 'Quality check results on field:... int32 \n", + "18 {'long_name': 'Northward wind component', 'uni... float32 \n", + "19 {'long_name': 'Quality check results on field:... int32 \n", + "20 {'long_name': 'Wind status', 'units': 'unitless'} float32 \n", + "21 {'long_name': 'Ascent rate', 'units': 'm/s', '... float32 \n", + "22 {'long_name': 'Quality check results on field:... int32 \n", + "23 {'long_name': 'Smoothed original relative humi... float32 \n", + "24 {'long_name': 'Quality check results on field:... int32 \n", + "25 {'long_name': 'Dry bias corrected relative hum... float32 \n", + "26 {'long_name': 'Quality check results on field:... int32 \n", + "27 {'long_name': 'Final corrected ambient relativ... float32 \n", + "28 {'long_name': 'Quality check results on field:... int32 \n", + "29 {'long_name': 'Scaled final corrected ambient ... float32 \n", + "30 {'long_name': 'Quality check results on field:... int32 \n", + "31 {'long_name': 'Scaled dewpoint temperature', '... float32 \n", + "32 {'long_name': 'Quality check results on field:... int32 \n", + "33 {'long_name': 'North latitude', 'units': 'degr... float32 \n", + "34 {'long_name': 'East longitude', 'units': 'degr... float32 \n", + "35 {'long_name': 'Altitude above mean sea level',... float32 " + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# bonus: indivdual variable info table (collect and display variable info as a tabular format)\n", + "df_info = pd.DataFrame()\n", + "df_info[\"var_name\"] = list(ds.variables)\n", + "df_info[\"dims\"] = df_info.var_name.apply(lambda x: ds[x].dims)\n", + "df_info[\"is_dim\"] = df_info.var_name.apply(lambda x: x in ds.dims)\n", + "df_info[\"n_dim\"] = df_info.var_name.apply(lambda x: len(ds[x].dims))\n", + "df_info[\"attrs\"] = df_info.var_name.apply(lambda x: ds[x].attrs)\n", + "df_info[\"dtype\"] = df_info.var_name.apply(lambda x: ds[x].data.dtype)\n", + "df_info" + ] + }, + { + "cell_type": "markdown", + "id": "e6a36b96", + "metadata": {}, + "source": [ + "### Data cleaning/Preprocessing (skipped)\n", + "Data cleaning and preprocessing is an important stage in the the data analysis pipeline. However it will be out of the scope of this notebook. For more information about data cleaning and preprocessing basics, here are some references. \n", + "* [Xarray Fundamentals](https://earth-env-data-science.github.io/lectures/xarray/xarray.html)\n", + "* [Xarray Tutorial — Pangeo Gallery documentation](http://gallery.pangeo.io/repos/pangeo-data/pangeo-tutorial-gallery/xarray.html)\n", + "* [Pythonic Data Cleaning With pandas and NumPy](https://realpython.com/python-data-cleaning-numpy-pandas/)\n", + "* [Pandas - Cleaning Data](https://www.w3schools.com/python/pandas/pandas_cleaning.asp)" + ] + }, + { + "cell_type": "markdown", + "id": "ae6e1a69", + "metadata": {}, + "source": [ + "### Plotting\n", + "Data visualization (or plotting) is another important data analysis topic and deserves its own discussion. Here in this notebook we will only demonstrate basic tool for simple data visualization tasks.\n", + "\n", + "Here are some reference you might find useful:\n", + "* xarray plotting: https://docs.xarray.dev/en/stable/user-guide/plotting.html\n", + "* Atmospheric Community Toolkit (ACT): https://arm-doe.github.io/ACT/index.html\n", + "\n", + "Note: this notebook is auto-generated using a template. It uses a general idea to select variable(s) to plot and is not customized to each indivdual notebook. Feel free to change the variables in intrrests, especially certain figure is failed to plot." + ] + }, + { + "cell_type": "markdown", + "id": "7e417fb2", + "metadata": {}, + "source": [ + "#### 1-dimenssional basic time series plot\n", + "\n", + "For the following plot we would like to find variables such that\n", + "* it has one and only one dimession\n", + "* \"time\" is its coordinate variable\n", + "* it is not a dimenssion itself,\n", + "* it is not a special variable with substrings within [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "\n", + "Please see the following example in action. (For more details about pandas filtering, please see the following references.)\n", + "* [pandas: multiple conditions while indexing data frame](https://stackoverflow.com/questions/22591174/pandas-multiple-conditions-while-indexing-data-frame-unexpected-behavior)\n", + "* [pandas.DataFrame.apply](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.apply.html)\n", + "* [How to test if a string contains one of the substrings in a list...](https://stackoverflow.com/questions/26577516/how-to-test-if-a-string-contains-one-of-the-substrings-in-a-list-in-pandas" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "30edac2d", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
var_namedimsis_dimn_dimattrsdtype
4pres(time,)False1{'long_name': 'Barometric pressure', 'units': ...float32
6tdry(time,)False1{'long_name': 'Dry bulb temperature', 'units':...float32
8dp(time,)False1{'long_name': 'Dewpoint temperature', 'units':...float32
10wspd(time,)False1{'long_name': 'Wind speed', 'units': 'm/s', 'v...float32
12deg(time,)False1{'long_name': 'Wind direction', 'units': 'deg'...float32
14rh(time,)False1{'long_name': 'Relative humidity', 'units': '%...float32
16u_wind(time,)False1{'long_name': 'Eastward wind component', 'unit...float32
18v_wind(time,)False1{'long_name': 'Northward wind component', 'uni...float32
20wstat(time,)False1{'long_name': 'Wind status', 'units': 'unitless'}float32
21asc(time,)False1{'long_name': 'Ascent rate', 'units': 'm/s', '...float32
23rh_smooth(time,)False1{'long_name': 'Smoothed original relative humi...float32
25rh_biased(time,)False1{'long_name': 'Dry bias corrected relative hum...float32
27rh_adjust(time,)False1{'long_name': 'Final corrected ambient relativ...float32
29rh_scaled(time,)False1{'long_name': 'Scaled final corrected ambient ...float32
31dp_scaled(time,)False1{'long_name': 'Scaled dewpoint temperature', '...float32
\n", + "
" + ], + "text/plain": [ + " var_name dims is_dim n_dim \\\n", + "4 pres (time,) False 1 \n", + "6 tdry (time,) False 1 \n", + "8 dp (time,) False 1 \n", + "10 wspd (time,) False 1 \n", + "12 deg (time,) False 1 \n", + "14 rh (time,) False 1 \n", + "16 u_wind (time,) False 1 \n", + "18 v_wind (time,) False 1 \n", + "20 wstat (time,) False 1 \n", + "21 asc (time,) False 1 \n", + "23 rh_smooth (time,) False 1 \n", + "25 rh_biased (time,) False 1 \n", + "27 rh_adjust (time,) False 1 \n", + "29 rh_scaled (time,) False 1 \n", + "31 dp_scaled (time,) False 1 \n", + "\n", + " attrs dtype \n", + "4 {'long_name': 'Barometric pressure', 'units': ... float32 \n", + "6 {'long_name': 'Dry bulb temperature', 'units':... float32 \n", + "8 {'long_name': 'Dewpoint temperature', 'units':... float32 \n", + "10 {'long_name': 'Wind speed', 'units': 'm/s', 'v... float32 \n", + "12 {'long_name': 'Wind direction', 'units': 'deg'... float32 \n", + "14 {'long_name': 'Relative humidity', 'units': '%... float32 \n", + "16 {'long_name': 'Eastward wind component', 'unit... float32 \n", + "18 {'long_name': 'Northward wind component', 'uni... float32 \n", + "20 {'long_name': 'Wind status', 'units': 'unitless'} float32 \n", + "21 {'long_name': 'Ascent rate', 'units': 'm/s', '... float32 \n", + "23 {'long_name': 'Smoothed original relative humi... float32 \n", + "25 {'long_name': 'Dry bias corrected relative hum... float32 \n", + "27 {'long_name': 'Final corrected ambient relativ... float32 \n", + "29 {'long_name': 'Scaled final corrected ambient ... float32 \n", + "31 {'long_name': 'Scaled dewpoint temperature', '... float32 " + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# (pandas query) filter n_dim==1, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter = df_info[(df_info.n_dim == 1) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "4777a659", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_1d = df_filter.var_name.values[0]\n", + " var_1d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "fb5ae985", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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pVIplEqlaNq0qZg/f36t1qxPD2qPimPk7r/giouLxdy5c0WjRo3EwYMHhRD/TQjNy8sTK1euNOpj5EHtkZSUJIT4r4coMzNTZGZmauzXpk2bKk/0xqo6x4cQ5ncO+eijjyr9sjbnc8j92sNUziEMREZuzpw5QiaTibFjx2psv337tujdu7fo2LGjetuiRYuEr6+vsLGxEV27dhX//POPWLJkiZDL5eLatWu1XbrezJ07VygUCvH000+LixcvqkNiamqq6N27t+jQoYN63xkzZojw8HCxfv169babN2+KgIAAsXbtWiGE8XfzP6w97j5GhPjv8545c0YMHjxYPPnkk+LmzZti5MiR4q+//qr1+nXtUduj4r83b94UzZo1Ex999JHGdmOlTXuYyzlk1qxZwsrKSshkMvHbb78JIcqDTsV51dzOIQ9rD1M5hzAQGam//vpLeHt7iwYNGogtW7ZUel6lUomvv/5azJgxQ70uxo4dO0SvXr3Eb7/9pk7qqampIiQkRMyePbtW69eX//3vf6Jx48bijz/+ECUlJRrj+iqVSnz11VdixowZIjc3VwghRFxcnBg7dqxwc3MTf/zxh7h8+bJYvHixaNKkifpSWWNW3fYoLCys8vVffvmlsLW1FVZWVqJevXoiLi6utkrXi5q2h1KpFAsWLBCdO3cWly5dqq2y9aa67WEu55D169cLHx8f0aBBA/HXX3+Jbt26iSlTpqifv/u8ag7nEG3awxTOIQxERujgwYPC29tbdO/eXb0tLS1NFBYWaiwHX1BQUOm1FZPe7nb79m291FmblEqlSE1NFe3btxebNm0SQggRHx8vDh8+LBITE0VxcbEQQqhP7HdLTU0VI0eOFMHBwaJu3brC19dX/R7GqibtIUR51/aff/4pfH19Rd26dcXmzZtrrXZ9qEl7FBcXiwMHDogvv/xStGzZUgQGBopt27bVav26VpP2MNVzyPvvvy9sbGzE4sWL1duefPJJMWLECFFYWKju9aiYJ3Q3UzyH1KQ9hDDOcwgDkRFKTU0VU6ZMEZ07dxbXrl0T77//vggPDxft2rUTAwcOFBcuXKj0moqD1xRXRa34bHFxccLb21skJyeLuXPnCm9vb9G6dWsREBCg8dfr/doiNTVVREVF1V7hevKo7XF3t35hYaHo16+fiIiIqN3i9aCm7ZGVlSVWrFghevbs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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " ds[var_1d].plot()\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "9e41066e", + "metadata": {}, + "source": [ + "#### 2-dimenssional basic plot" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "id": "09bd4adc", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
var_namedimsis_dimn_dimattrsdtype
\n", + "
" + ], + "text/plain": [ + "Empty DataFrame\n", + "Columns: [var_name, dims, is_dim, n_dim, attrs, dtype]\n", + "Index: []" + ] + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# (pandas query) filter n_dim==2, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_2 = df_info[(df_info.n_dim == 2) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_2" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "id": "2cdea1a9", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "index 0 is out of bounds for axis 0 with size 0\n" + ] + } + ], + "source": [ + "try:\n", + " var_2d = df_filter_2.var_name.values[0]\n", + " var_2d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "id": "f5f48879", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "name 'var_2d' is not defined\n" + ] + } + ], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " print(ds[var_2d].dims)\n", + " # ds[var_2d].plot()\n", + "\n", + " # conventionally, use \"time\" as x-axis\n", + " ds[var_2d].plot(x=\"time\")\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "7554b2a6", + "metadata": {}, + "source": [ + "#### qc-plotting (optional)\n", + "\n", + "Note: act qc plotting has more strict requirement, one of them is the associated qc variable need to have \"flag_masks\" attributes. Which is added by using ds = act.io.armfiles.read_netcdf(files_list), then ds.clean.cleanup()" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "id": "71c2096f", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n" + ] + } + ], + "source": [ + "try:\n", + " ds_act = act.io.armfiles.read_netcdf(full_path)\n", + " print(type(ds_act))\n", + " ds_act.clean.cleanup()\n", + "\n", + " # or \n", + " # ds.clean.cleanup()\n", + "except Exception as e:\n", + " print(\"ERROR\", e)\n", + " ds_act = ds" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "id": "844f8505", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
var_namedimsis_dimn_dimattrsdtype
4pres(time,)False1{'long_name': 'Barometric pressure', 'units': ...float32
6tdry(time,)False1{'long_name': 'Dry bulb temperature', 'units':...float32
8dp(time,)False1{'long_name': 'Dewpoint temperature', 'units':...float32
10wspd(time,)False1{'long_name': 'Wind speed', 'units': 'm/s', 'v...float32
12deg(time,)False1{'long_name': 'Wind direction', 'units': 'deg'...float32
14rh(time,)False1{'long_name': 'Relative humidity', 'units': '%...float32
16u_wind(time,)False1{'long_name': 'Eastward wind component', 'unit...float32
18v_wind(time,)False1{'long_name': 'Northward wind component', 'uni...float32
21asc(time,)False1{'long_name': 'Ascent rate', 'units': 'm/s', '...float32
23rh_smooth(time,)False1{'long_name': 'Smoothed original relative humi...float32
25rh_biased(time,)False1{'long_name': 'Dry bias corrected relative hum...float32
27rh_adjust(time,)False1{'long_name': 'Final corrected ambient relativ...float32
29rh_scaled(time,)False1{'long_name': 'Scaled final corrected ambient ...float32
31dp_scaled(time,)False1{'long_name': 'Scaled dewpoint temperature', '...float32
\n", + "
" + ], + "text/plain": [ + " var_name dims is_dim n_dim \\\n", + "4 pres (time,) False 1 \n", + "6 tdry (time,) False 1 \n", + "8 dp (time,) False 1 \n", + "10 wspd (time,) False 1 \n", + "12 deg (time,) False 1 \n", + "14 rh (time,) False 1 \n", + "16 u_wind (time,) False 1 \n", + "18 v_wind (time,) False 1 \n", + "21 asc (time,) False 1 \n", + "23 rh_smooth (time,) False 1 \n", + "25 rh_biased (time,) False 1 \n", + "27 rh_adjust (time,) False 1 \n", + "29 rh_scaled (time,) False 1 \n", + "31 dp_scaled (time,) False 1 \n", + "\n", + " attrs dtype \n", + "4 {'long_name': 'Barometric pressure', 'units': ... float32 \n", + "6 {'long_name': 'Dry bulb temperature', 'units':... float32 \n", + "8 {'long_name': 'Dewpoint temperature', 'units':... float32 \n", + "10 {'long_name': 'Wind speed', 'units': 'm/s', 'v... float32 \n", + "12 {'long_name': 'Wind direction', 'units': 'deg'... float32 \n", + "14 {'long_name': 'Relative humidity', 'units': '%... float32 \n", + "16 {'long_name': 'Eastward wind component', 'unit... float32 \n", + "18 {'long_name': 'Northward wind component', 'uni... float32 \n", + "21 {'long_name': 'Ascent rate', 'units': 'm/s', '... float32 \n", + "23 {'long_name': 'Smoothed original relative humi... float32 \n", + "25 {'long_name': 'Dry bias corrected relative hum... float32 \n", + "27 {'long_name': 'Final corrected ambient relativ... float32 \n", + "29 {'long_name': 'Scaled final corrected ambient ... float32 \n", + "31 {'long_name': 'Scaled dewpoint temperature', '... float32 " + ] + }, + "execution_count": 30, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Filter valid variables for ACT qc plotting\n", + "condition = (\"qc_\" + df_info.var_name).apply(lambda x: ds_act[x].attrs.get(\"flag_masks\") is not None \n", + " if x in list(ds.variables) else False)\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_3 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings))) &\n", + " condition\n", + " ]\n", + "df_filter_3" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "id": "ecd38cc6", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "pres\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/kefeimo/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/act/utils/datetime_utils.py:136: FutureWarning: Unlike other reduction functions (e.g. `skew`, `kurtosis`), the default behavior of `mode` typically preserves the axis it acts along. In SciPy 1.11.0, this behavior will change: the default value of `keepdims` will become False, the `axis` over which the statistic is taken will be eliminated, and the value None will no longer be accepted. Set `keepdims` to True or False to avoid this warning.\n", + " mode = stats.mode(np.diff(time))\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAzUAAANCCAYAAABfy+KVAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAA9hAAAPYQGoP6dpAACuSklEQVR4nOzdaXgUVfr38V+TPSGEPQuGgIgggsiisoigbIos6rgN6oAPKoobboyoI8EFFBGZwZ1BwN2/KOooIqCASoICgoIICrJKIgIhYc16nheVbtJJulMJnaQ7+X6uKxd01amqU3cqXXXXOXXKYYwxAgAAAIAAVae6KwAAAAAAJ4OkBgAAAEBAI6kBAAAAENBIagAAAAAENJIaAAAAAAGNpAYAAABAQCOpAQAAABDQSGoAAAAABDSSGgAAAAABjaQGACrZypUrddVVVyk+Pl6hoaGKj4/X1VdfrVWrVnlc5qefftKNN96oli1bKjw8XHXr1lXnzp01ZcoUHThwoAprf/KSk5PlcDjcpr344ouaM2dO9VToJH355Zfq2rWroqKi5HA49NFHH2nOnDlyOBzavn17uddXnmX79OmjPn36lHsbAFDTBVd3BQCgJpsxY4bGjh2rc889V1OmTFFSUpJ27typF154Qd26ddNLL72kW265xW2ZmTNnasyYMWrTpo0eeOABtWvXTrm5uVq9erVefvllpaamav78+dW0R77x4osvqnHjxho5cmR1V6VcjDG6+uqrdfrpp+uTTz5RVFSU2rRpo7y8PKWmpio+Pr66qwgAtRJJDQBUkhUrVmjs2LEaNGiQ5s+fr+DgE1+51157rS6//HKNGTNGnTp10jnnnCNJSk1N1W233ab+/fvro48+UlhYmGuZ/v3767777tPChQvLXZejR48qMjLy5HeqltuzZ48OHDigyy+/XH379nWb16RJk2qqVdXgGALgz+h+BgCVZPLkyXI4HHrppZfcEhpJCg4O1osvvugq5zRp0iQ5HA69+uqrbgmNU2hoqIYOHep1uyNHjlTdunW1fv16DRgwQNHR0a4L8JycHD3xxBNq27atwsLC1KRJE914443666+/3Nbx1VdfqU+fPmrUqJEiIiLUvHlz/e1vf9PRo0clScuWLZPD4dCyZcvcltu+fbscDofXrmUtWrTQzz//rOXLl8vhcMjhcKhFixaSpIKCAj3xxBNq06aNIiIiVL9+fZ111ln697//7XWfJWnnzp26/vrr1bRpU4WFhemMM87Qs88+q4KCghL1mzp1qqZNm6aWLVuqbt266t69u1auXOl1/cnJyTrllFMkSf/85z/d6u2pC9mSJUvUt29f1atXT5GRkerZs6e+/PLLMvfFGONq2QsPD1fnzp31+eefl7mcN87f2Ztvvql7771XcXFxioiIUO/evbV27Vq3slVxDAGAL9FSAwCVID8/X0uXLlXXrl1dF8LFJSYmqkuXLlqyZIkKCgpkjNFXX32lLl26KDEx8aS2n5OTo6FDh2r06NF68MEHlZeXp4KCAg0bNkzffPONxo0bpx49emjHjh2aMGGC+vTpo9WrVysiIkLbt2/XpZdeql69eum1115T/fr19ccff2jhwoXKyck56bv18+fP15VXXqmYmBhXYudM4KZMmaLk5GQ98sgjuuCCC5Sbm6tNmzbp4MGDXtf5119/qUePHsrJydHjjz+uFi1a6NNPP9X999+vrVu3urbj9MILL6ht27aaPn26JOlf//qXBg0apG3btikmJqbUbdx0003q2LGjrrjiCt15550aPnx4qYmn05tvvql//OMfGjZsmObOnauQkBC98sorGjhwoL744osSLT1FTZw4URMnTtSoUaN05ZVXateuXbr55puVn5+vNm3auJXt06ePli9fLmOM1xg5PfTQQ+rcubP++9//KjMzU8nJyerTp4/Wrl2rU0891VXOn48hACjBAAB8Lj093Ugy1157rddy11xzjZFk/vrrL9vLlGXEiBFGknnttdfcpr/zzjtGkvnggw/cpq9atcpIMi+++KIxxph58+YZSWbdunUet7F06VIjySxdutRt+rZt24wkM3v2bNe0CRMmmOKnmzPPPNP07t27xHoHDx5szj77bBt76e7BBx80ksx3333nNv22224zDofDbN682a1+HTp0MHl5ea5y33//vZFk3nnnHa/bcS7/zDPPuE2fPXu2kWS2bdtmjDHmyJEjpmHDhmbIkCFu5fLz803Hjh3Nueee63HZjIwMEx4ebi6//HK3ZVesWGEklYjbRRddZIKCgrzW25gTv7POnTubgoIC1/Tt27ebkJAQc9NNN7mmVcUxBAC+RPczAKhGpvDuevHRwXzhb3/7m9vnTz/9VPXr19eQIUOUl5fn+jn77LMVFxfn6kp29tlnKzQ0VLfccovmzp2r33//3ed18+Tcc8/Vjz/+qDFjxuiLL75QVlaWreW++uortWvXTueee67b9JEjR7pawIq69NJLFRQU5Pp81llnSZJ27NhxkntgSUlJ0YEDBzRixAi3WBcUFOjiiy/WqlWrdOTIkVKXTU1N1fHjx3Xddde5Te/Ro4eSkpJKlP/yyy+Vl5dnu27Dhw93O96SkpLUo0cPLV26tETZQDyGANROJDUAUAkaN26syMhIbdu2zWu57du3KyIiQo0aNbK9jB2RkZGqV6+e27Q///xTBw8eVGhoqEJCQtx+0tPTtW/fPklSq1attGTJEjVt2lS33367WrVqpVatWtl6ruVkjR8/XlOnTtXKlSt1ySWXqFGjRurbt69Wr17tdbn9+/eXOvJYQkKCa35RjRo1cvvs7EZ27Nixk6m+y59//ilJuvLKK0vE+umnn5YxxuPQ3M66xsXFlZhX2rTy8rTe4jEK1GMIQO3EMzUAUAmCgoJ00UUX6fPPP9fu3btLfa5m9+7dWrNmjS6++GLXMn379vW6jF2ltfw0btxYjRo18jh6WnR0tOv/vXr1Uq9evZSfn6/Vq1e7hqaOjY3Vtddeq/DwcElSdna22zqcF7UVFRwcrHvvvVf33nuvDh48qCVLluihhx7SwIEDtWvXLo/PYjRq1EhpaWklpu/Zs0eSte9Vybm9GTNmqFu3bqWWiY2NLXW6M+FKT08vMS89Pd01OEFFeVpv8USvso8hAPAlWmoAoJI8+OCDMsZozJgxys/Pd5uXn5+v2267Tfn5+br77rtd08ePHy9jjG6++Wbl5OSUWGdubq7+97//Vag+gwcP1v79+5Wfn6+uXbuW+Cn+ALpkJVrnnXeeXnjhBUnSDz/8IEmuC+uffvrJrfwnn3xiqy5hYWFltorUr19fV155pW6//XYdOHDA68sp+/btq40bN7rq5/T666/L4XDowgsvtFUvX+nZs6fq16+vjRs3lhrrrl27KjQ0tNRlu3XrpvDwcL311ltu01NSUnzSPe6dd95xG1Rgx44dSklJsfVST18eQwDgS7TUAEAl6dmzp6ZPn667775b559/vu644w41b97c9fLN1NRUJScnq3///q5lunfvrpdeekljxoxRly5ddNttt+nMM89Ubm6u1q5dq1dffVXt27fXkCFDyl2fa6+9Vm+99ZYGDRqku+++W+eee65CQkK0e/duLV26VMOGDdPll1+ul19+WV999ZUuvfRSNW/eXMePH9drr70mSerXr58kq7tSv379NHnyZDVo0EBJSUn68ssv9eGHH9qqS4cOHfTuu+/qvffe06mnnqrw8HB16NBBQ4YMUfv27dW1a1c1adJEO3bs0PTp05WUlKTWrVt7XN8999yj119/XZdeeqkee+wxJSUl6bPPPtOLL76o2267Taeffnq543Uy6tatqxkzZmjEiBE6cOCArrzySjVt2lR//fWXfvzxR/3111966aWXSl22QYMGuv/++/XEE0/opptu0lVXXaVdu3YpOTm51K5jffv21fLly20/V7N3715dfvnluvnmm5WZmakJEyYoPDxc48ePL3NZXx5DAOBT1TlKAQDUBikpKeZvf/ubiY2NNXXq1DGSTHh4uPnss888LrNu3TozYsQI07x5cxMaGmqioqJMp06dzKOPPmr27t3rdXsjRowwUVFRpc7Lzc01U6dONR07djTh4eGmbt26pm3btmb06NHmt99+M8YYk5qaai6//HKTlJRkwsLCTKNGjUzv3r3NJ5984rautLQ0c+WVV5qGDRuamJgYc/3115vVq1fbGv1s+/btZsCAASY6OtpIMklJScYYY5599lnTo0cP07hxYxMaGmqaN29uRo0aZbZv3+51n40xZseOHWb48OGmUaNGJiQkxLRp08Y888wzJj8/31XG0+hlxhgjyUyYMMHrNuyOfua0fPlyc+mll5qGDRuakJAQ06xZM3PppZea999/3+uyBQUFZvLkySYxMdGEhoaas846y/zvf/8zvXv3LjH6We/evUvEtzTO0c/eeOMNc9ddd5kmTZqYsLAw06tXL7N69Wq3slV1DAGArziMsTmwPQDAJ15//XWNGDFC48aN09NPP13d1UEtsWzZMl144YV6//33deWVV1Z3dQDAp+h+BgBV7B//+IfS0tL04IMPKioqSo8++mh1VwkAgIBGUgMA1eCf//yn/vnPf1Z3NQAAqBHofgYAAAAgoDGkMwAAAICARlIDAAAAIKCR1AAAAAAIaAwUYFNBQYH27Nmj6OhoORyO6q4OAAAAUKMZY3To0CElJCSoTh3vbTEkNTbt2bNHiYmJ1V0NAAAAoFbZtWuXTjnlFK9lSGpsio6OlmQFtV69etVcG0tubq4WLVqkAQMGKCQk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5sEHHzTGWBfrpR0kdowbN85cf/31Zvbs2SW+4MeNG2fatm3rNm306NGmW7duFdpWZauMODm/3NeuXeuxTE2L0zXXXGOuv/76cq3zxRdfNDExMeb48eOuaZMnTzYJCQmmoKDAGBN4cTKmcmJVlKdjMtBiVdlxcho0aJC58cYbXZ+JU+nuuecec/7557s+E6cTatI5z5jKiVVtPO8VN2zYMHPRRRd5XWdNPO9VRpyK8qdzHt3PqkFOTo7WrFmjAQMGuE0fMGCAUlJSXJ+XLVumpk2b6vTTT9fNN9+svXv3upUfOXKk+vTp4zbtq6++0vvvv68XXnih1G2npqaW2O7AgQO1evXqEl0bqltlxkmShg4dqqZNm6pnz56aN2+e27yaFKeCggJ99tlnOv300zVw4EA1bdpU5513nj766CO38sXjlJqaqt69e7u9bGvgwIHas2ePtm/f7ioTKHGSKi9WdgRSrKoyTpmZmWrYsKHrM3EqacuWLVq4cKF69+7tmkacLDXpnCdV/jFVW857xf3555/67LPPNGrUKLfpNf28V1lxsqM64kRSUw327dun/Px8xcbGuk2PjY1Venq6JOmSSy7RW2+9pa+++krPPvusVq1apYsuukjZ2dmu8vHx8WrevLnr8/79+zVy5EjNmTNH9erVK3Xb6enppW43Ly9P+/bt89Uu+kRlxalu3bqaNm2a5s2bpwULFqhv37665ppr9Oabb7rK1KQ47d27V4cPH9ZTTz2liy++WIsWLdLll1+uK664QsuXL3eVLx4nTzFwzvNWxh/jJFVerOwIpFhVVZzmzZunVatW6cYbb3RNI04n9OjRQ+Hh4WrdurV69eqlxx57zDWPONW8c55UebGqbee94ubOnavo6GhdccUVbtNr+nmvsuJkR3XEKbhS1gpbHA6H22djjGvaNddc45revn17de3aVUlJSfrss89cB9vkyZPdlr/55ps1fPhwXXDBBeXebmnT/YWv49S4cWPdc889rs9du3ZVRkaGpkyZouuvv97rdkub7i88xamgoECSNGzYMNd+n3322UpJSdHLL7/suvtbPE6e1ll8eqDFSaqcWFV0u6VN9xeVGadly5Zp5MiRmjlzps4888wyt1vadH9RWXF67733dOjQIf3444964IEHNHXqVI0bN87rdkub7i98Haeaes6TfB+r2nbeK+61117Tddddp/DwcLfpteW8Vxlxquh2S5vuK7TUVIPGjRsrKCioRJa8d+/eElmtU3x8vJKSkvTbb795XO9XX32lqVOnKjg4WMHBwRo1apQyMzMVHBys1157TZIUFxdX6naDg4PVqFGjk9wz36qsOJWmW7dubsvUpDg1btxYwcHBateundv8M844w+toOZ5iIJ24cxVIcZIqL1Z2BFKsKjtOy5cv15AhQzRt2jT94x//cJtHnE5ITExUu3bt9Pe//11PPfWUkpOTlZ+fL4k4STXvnCdV7XdUTT7vFfXNN99o8+bNuummm8pcb00771VWnOyojjiR1FSD0NBQdenSRYsXL3abvnjxYvXo0aPUZfbv369du3YpPj7e43pTU1O1bt06189jjz2m6OhorVu3TpdffrkkqXv37iW2u2jRInXt2lUhISEnuWe+VVlxKs3atWvdlqlJcQoNDdU555yjzZs3u83/9ddflZSU5HG93bt319dff62cnBzXtEWLFikhIUEtWrRwlQmUOEmVFys7AilWlRmnZcuW6dJLL9VTTz2lW265pcR84lQ6Y4xyc3NddzqJU80750lVe0zV5PNeUbNmzVKXLl3UsWPHMtdb0857lRUnO6olTpU2BAG8cg6xN2vWLLNx40YzduxYExUVZbZv324OHTpk7rvvPpOSkmK2bdtmli5darp3726aNWtmsrKyXOt48MEHzQ033OBxG6WNBOMcYu+ee+4xGzduNLNmzQqIoQh9Gac5c+aYt956y2zcuNFs2rTJPPPMMyYkJMRMmzbNVaYmxckYYz788EMTEhJiXn31VfPbb7+ZGTNmmKCgIPPNN9+41lE8TgcPHjSxsbHm73//u1m/fr358MMPTb169Uod2jJQ4mRM5cTKGGN+/vlns3btWjNkyBDTp08fs3btWreRhgItVpURp6VLl5rIyEgzfvx4j8OwEydj3nzzTfPee++ZjRs3mq1bt5r/+7//M82aNTPXXXedqwxxKqkmnPOMqZxY1cbznjHWEM2RkZHmpZdeKnUdteG8VxlxMsY/z3kkNdXohRdeMElJSSY0NNR07tzZLF++3BhjzNGjR82AAQNMkyZNTEhIiGnevLkZMWKE2blzp9vyI0aMML179/a4/tK+4I0xZtmyZaZTp04mNDTUtGjRwuNB7C98Hac5c+aYM844w0RGRpro6GjTpUsX88Ybb5TYbk2Jk9OsWbPMaaedZsLDw03Hjh3NRx995Da/tOPpp59+Mr169TJhYWEmLi7OJCcnu4a1dAq0OBlTObFKSkoykkr8FBVosfJ1nEaMGFFqjIrHsrbH6d133zWdO3c2devWNVFRUaZdu3Zm0qRJ5tixY27L1fY4FVdTznnG+D5WtfW898orr5iIiAhz8ODBUpevLee9yoiTP57zHMYUe00qAAAAAAQQnqkBAAAAENBIagAAAAAENJIaAAAAAAGNpAYAAABAQCOpAQAAABDQSGoAAAAABDSSGgAAAAABjaQGAAAAQEAjqQEQEObMmaP69etXdzUAAIAfIqkBIEnq06ePxo4dW2L6Rx99JIfD4SrjcDg8/rRo0UKSlJ6erjvvvFOnnnqqwsLClJiYqCFDhujLL78sddstWrTwut4+ffrommuu0a+//lpZu19hDodDH330kU/WtX37djkcDq1bt84n6/MkOTlZZ599dpnlZs6cqV69eqlBgwZq0KCB+vXrp++//75EuRdffFEtW7ZUeHi4unTpom+++cZtvjFGycnJSkhIUEREhPr06aOff/7ZrUx6erpuuOEGxcXFKSoqSp07d9a8efPKrOPOnTs1ZMgQRUVFqXHjxrrrrruUk5NTYvtTp07V6aef7joeJ02aVOa6P/jgA7Vr105hYWFq166d5s+f7zb/66+/1pAhQ5SQkFDiOHD+Lr39JCcn29oHT+tauHCh1/onJyerbdu2ioqKcv3+vvvuO7cypf1NX3vttWXGZvny5erSpYvCw8N16qmn6uWXXy53/Eqzfv169e7dWxEREWrWrJkee+wxGWMqZdu+OG6zs7N15513qnHjxoqKitLQoUO1e/dutzIZGRm64YYbFBMTo5iYGN1www06ePCgWxk7x7Gd2AC1mgEAY0zv3r3N3XffXWL6/PnzjfOrYv/+/SYtLc2kpaWZ77//3kgyS5YscU3bu3ev2bZtm0lISDDt2rUz77//vtm8ebPZsGGDefbZZ02bNm1K3fbevXtd6/jggw+MJLN582bXtP3791fmrp8USWb+/PknvZ7s7Gyzbds2I8msXbv2pNfnzYQJE0zHjh3LLDd8+HDzwgsvmLVr15pffvnF3HjjjSYmJsbs3r3bVebdd981ISEhZubMmWbjxo3m7rvvNlFRUWbHjh2uMk899ZSJjo42H3zwgVm/fr255pprTHx8vMnKynKV6devnznnnHPMd999Z7Zu3Woef/xxU6dOHfPDDz94rF9eXp5p3769ufDCC80PP/xgFi9ebBISEswdd9zhVu7OO+80bdq0MR9//LH5/fffzdq1a83ixYu97ntKSooJCgoykyZNMr/88ouZNGmSCQ4ONitXrnSVWbBggXn44Yddx2zR4yAvL891/KalpZn77rvPnHnmmW7TDh06ZGsfnMdF0b+1tLQ0k52d7XUf3nrrLbN48WKzdetWs2HDBjNq1ChTr149s3fvXleZ3r17m5tvvtltvQcPHvS63t9//91ERkaau+++22zcuNHMnDnThISEmHnz5pUrfsVlZmaa2NhYc+2115r169ebDz74wERHR5upU6f6fNu+Om5vvfVW06xZM7N48WLzww8/mAsvvNB07NjR5OXlucpcfPHFpn379iYlJcWkpKSY9u3bm8GDB7vm2zkG7MQGqO1IagAYY+wlNUV5ugC/5JJLTLNmzczhw4dLLJORkVFmPZYuXWoklSg7e/ZsExMT4/rsvDCfNWuWSUxMNFFRUebWW281eXl55umnnzaxsbGmSZMm5oknnnBbz8GDB83NN99smjRpYqKjo82FF15o1q1b57E+2dnZ5vbbbzdxcXEmLCzMJCUlmUmTJhljjElKSjKSXD9JSUnGGGO2bNlihg4dapo2bWqioqJM165dS1xEJyUlmccff9yMGDHC1KtXz/zjH/9wW5ck07t3b1dMzjnnHBMZGWliYmJMjx49zPbt2z3Wedy4caZ169YmIiLCtGzZ0jzyyCMmJyfHFcfi25k9e7aX38gJeXl5Jjo62sydO9c17dxzzzW33nqrW7m2bduaBx980BhjTEFBgYmLizNPPfWUa/7x48dNTEyMefnll13ToqKizOuvv+62noYNG5r//ve/HuuzYMECU6dOHfPHH3+4pr3zzjsmLCzMZGZmGmOM2bhxowkODjabNm2ytY9OV199tbn44ovdpg0cONBce+21pZYvK7n1lEja2QdfJbuZmZmu5MjJ09+9N+PGjTNt27Z1mzZ69GjTrVs31+fyxs8YY1588UUTExNjjh8/7po2efJkk5CQYAoKCny6bV8ctwcPHjQhISHm3XffdZX5448/TJ06dczChQuNMdbxJ8ktoUpNTTWSXMeknWPATmyA2o7uZwB85sCBA1q4cKFuv/12RUVFlZjv62ditm7dqs8//1wLFy7UO++8o9dee02XXnqpdu/ereXLl+vpp5/WI488opUrV0qyupNceumlSk9P14IFC7RmzRp17txZffv21YEDB0rdxn/+8x998skn+r//+z9t3rxZb775pqub3apVqyRJs2fPVlpamuvz4cOHNWjQIC1ZskRr167VwIEDNWTIEO3cudNt3c8884zat2+vNWvW6F//+pera9eSJUuUlpamDz/8UHl5ebrsssvUu3dv/fTTT0pNTdUtt9zi6hJYmujoaM2ZM0cbN27Uv//9b82cOVPPPfecJOmaa67RfffdpzPPPFNpaWlKS0vTNddcYyveR48eVW5urho2bChJysnJ0Zo1azRgwAC3cgMGDFBKSookadu2bUpPT3crExYWpt69e7vKSNL555+v9957TwcOHFBBQYHeffddZWdnq0+fPh7rk5qaqvbt2yshIcE1beDAgcrOztaaNWskSf/73/906qmn6tNPP1XLli3VokUL3XTTTR5/30XXXXy/Bg4c6FZnX7CzD05Dhw5V06ZN1bNnzxJd85xd1JYtW1bqdnJycvTqq68qJiZGHTt2dJv31ltvqXHjxjrzzDN1//3369ChQ27z+/Tpo5EjR7rVubTYrF69Wrm5uV7LeItfamqqevfurbCwMLdl9uzZo+3bt/ts2746btesWaPc3Fy3MgkJCWrfvr2rTGpqqmJiYnTeeee5ynTr1k0xMTFuZco6BuzEBqjtgqu7AgBqji1btsgYo7Zt21bJ9goKCvTaa68pOjpa7dq104UXXqjNmzdrwYIFqlOnjtq0aaOnn35ay5YtU7du3bR06VKtX79ee/fudV0cTJ06VR999JHmzZunW265pcQ2du7cqdatW+v888+Xw+FQUlKSa16TJk0kWclaXFyca3rHjh3dLhyfeOIJzZ8/X5988onuuOMO1/SLLrpI999/v+uz8+KkUaNGrvUdOHBAmZmZGjx4sFq1aiVJOuOMM7zG5ZFHHnH9v0WLFrrvvvv03nvvady4cYqIiFDdunUVHBzsVmc7HnzwQTVr1kz9+vWTJO3bt0/5+fmKjY11KxcbG6v09HRJcv1bWpkdO3a4Pr/33nu65ppr1KhRIwUHBysyMlLz58937XNp0tPTS6y3QYMGCg0NdW33999/144dO/T+++/r9ddfV35+vu655x5deeWV+uqrr8q17qL75St29qFu3bqaNm2aevbsqTp16uiTTz7RNddco7lz5+r666+XJIWEhKhNmzaKjIx0W9enn36qa6+9VkePHlV8fLwWL16sxo0bu+Zfd911atmypeLi4rRhwwaNHz9eP/74oxYvXuwq07x5c8XHx3utc2xsrPLy8rRv3z7Fx8dXKH7p6emuGwZFl3HOa9mypU+27avjNj09XaGhoWrQoIHX9TRt2rTEvjZt2tStTFnHgJ3YALUdSQ0AnzGFD616a0XwpRYtWig6Otr1OTY2VkFBQapTp47btL1790qy7qwePnxYjRo1clvPsWPHtHXr1lK3MXLkSPXv319t2rTRxRdfrMGDB5e4w1vckSNHNHHiRH366afas2eP8vLydOzYsRItNV27di1zHxs2bKiRI0dq4MCB6t+/v/r166err77a7SKzuHnz5mn69OnasmWLDh8+rLy8PNWrV6/MbXkzZcoUvfPOO1q2bJnCw8Pd5hX/fRtjSkwrq8wjjzyijIwMLVmyRI0bN9ZHH32kq666St988406dOigSy65xPUgd1JSkuuB7dKOtaLrLigoUHZ2tl5//XWdfvrpkqRZs2apS5cu2rx5syIiItSuXTvXsg899JAeeugh2/vlC2XtQ+PGjXXPPfe45nXt2lUZGRmaMmWKK6lp1qyZNm3aVGI9F154odatW6d9+/Zp5syZuvrqq/Xdd9+5LrRvvvlmV9n27durdevW6tq1q3744Qd17txZkvT666+XWefS/vYrEr+Krrci2/ZVmeKKlynr91vRMlX9fQv4O7qfAZAk1atXT5mZmSWmHzx40PYFcevWreVwOPTLL7/4unqlCgkJcfvscDhKnVZQUCDJusCNj4/XunXr3H42b96sBx54oNRtdO7cWdu2bdPjjz+uY8eO6eqrr9aVV17ptV4PPPCAPvjgAz355JP65ptvtG7dOnXo0KHEaEalddErzezZs5WamqoePXrovffe0+mnn+7qUlfcypUrde211+qSSy7Rp59+qrVr1+rhhx8use3ymDp1qiZNmqRFixbprLPOck1v3LixgoKCStx937t3r+susrM1yFuZrVu36vnnn9drr72mvn37qmPHjpowYYK6du2qF154QZL03//+1/X7WrBggWvdxdebkZGh3Nxc17rj4+MVHBzsSmikEy1dO3fuVEJCgtuxcOutt3pcd9E6+4qdfShNt27d9Ntvv5W5/qioKJ122mnq1q2bZs2apeDgYM2aNctj+c6dOyskJMTruj3FJjg42HXDoCLx87SMJLfj6WS37avjNi4uTjk5OcrIyPBa5s8//yyxr3/99ZfXfSp+DNiJDVDbkdQAkCS1bdtWq1evLjF91apVatOmja11NGzYUAMHDtQLL7ygI0eOlJhffBjTqta5c2elp6crODhYp512mttP0S45xdWrV0/XXHONZs6cqffee08ffPCB65mMkJAQ5efnu5X/5ptvNHLkSF1++eXq0KGD4uLibPV7Dw0NlaQS65OkTp06afz48UpJSVH79u319ttvl7qOFStWKCkpSQ8//LC6du2q1q1bu3Xzcm6ntG2U5plnntHjjz+uhQsXlmhZCg0NVZcuXdy6KknS4sWL1aNHD0lydW0qWiYnJ0fLly93lTl69KgkubWwSVJQUJArIW3WrJnrd+XsAti9e3dt2LBBaWlprmUWLVqksLAwdenSRZLUs2dP5eXlubXEOYcGT0pKKnEsOJ8X6t69e4n9WrRokavOvmJnH0qzdu1ar611nhhjlJ2d7XH+zz//rNzcXK/r9hSbrl27um4qVCR+3bt319dff+2WgC9atEgJCQmurle+2LavjtsuXbooJCTErUxaWpo2bNjgKtO9e3dlZma6DYX+3XffKTMz061MWceAndgAtV7VjksAwF9t27bNREREmDFjxph169aZzZs3m+eff96EhYWZ//u//yu1vEoZken33383cXFxpl27dmbevHnm119/NRs3bjT//ve/S4xaVJryjn5W1IgRI8ywYcPcphUd3amgoMCcf/75pmPHjmbhwoVm27ZtZsWKFebhhx82q1atKrU+06ZNM++884755ZdfzObNm82oUaNMXFycyc/PN8YY07p1a3PbbbeZtLQ0c+DAAWOMMZdddpk5++yzzdq1a826devMkCFDTHR0tNsoU0lJSea5555z21Zubq6JiIgwTzzxhElPTzcHDx40v//+u3nwwQdNSkqK2b59u/niiy9Mw4YNzYsvvlhqfT/66CMTHBxs3nnnHbNlyxbz73//2zRs2NAtdm+99ZaJiooya9euNX/99ZfbiEpFPf300yY0NNTMmzevxFDETs6hcWfNmmU2btxoxo4da6KiotxGZ3vqqadMTEyM+fDDD8369evN3//+d7ehcXNycsxpp51mevXqZb777juzZcsWM3XqVONwOMxnn31Wat2MOTEUbt++fc0PP/xglixZYk455RS3oXDz8/NN586dzQUXXGB++OEHs3r1anPeeeeZ/v37e1yvMcasWLHCBAUFmaeeesr88ssv5qmnnioxLPChQ4fM2rVrzdq1a40kM23aNLN27Vq3YYGdPI1+Zmcf5syZY9566y2zceNGs2nTJvPMM8+YkJAQM23aNFeZ3bt3mzZt2pjvvvvOGGPM4cOHzfjx401qaqrZvn27WbNmjRk1apQJCwszGzZsMMZYo/RNnDjRrFq1ymzbts189tlnpm3btqZTp05uQxLfcMMNrlHBjDkxrPI999xjNm7caGbNmlViWGU78ZsxY4a56KKLXJ8PHjxoYmNjzd///nezfv168+GHH5p69eqVOqTzyW7bF8etMdaQzqeccopZsmSJ+eGHH8xFF11U6pDOZ511lklNTTWpqammQ4cOpQ7p7O0YsBMboLYjqQHgsnr1ajNw4EDTtGlTU69ePdO1a1fzzjvvlFrW2zCze/bsMbfffrtJSkoyoaGhplmzZmbo0KFm6dKlZdahMpMaY4zJysoyd955p0lISDAhISEmMTHRXHfddWbnzp2l1ufVV181Z599tomKijL16tVzXXg4ffLJJ+a0004zwcHBriGdt23bZi688EITERFhEhMTzfPPP1+iHqUlNcYYM3PmTJOYmGjq1KljevfubdLT081ll11m4uPjTWhoqElKSjKPPvqoK6kqzQMPPGAaNWpk6tata6655hrz3HPPucXu+PHj5m9/+5upX7++1yGdiw9Z7fyZMGGCW7kXXnjB9bvu3LmzWb58udv8goICM2HCBNew2BdccIFZv369W5lff/3VXHHFFaZp06YmMjLSnHXWWSWGeC7Njh07zKWXXmoiIiJMw4YNzR133FEiSfvjjz/MFVdcYerWrWtiY2PNyJEjbb376P333zdt2rQxISEhpm3btuaDDz5wm+88Vov/jBgxosS6vL0bqKx9mDNnjjnjjDNMZGSkiY6ONl26dDFvvPGG2zqcf4/Ov7Fjx46Zyy+/3CQkJJjQ0FATHx9vhg4dar7//nvXMjt37jQXXHCBadiwoQkNDTWtWrUyd911V4nY9O7du8Q+LVu2zHTq1MmEhoaaFi1a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e5E+b1xcnObPn1/nNlviea+p63TGnvMcmsXSpUtdQkKCW7Rokdu5c6e7//77Xdu2bd3evXudc85NnDjRjR492hUVFYVfhw4dimrbx48fd4MHD3bXX3+9S0tLqzLv008/dW3atHH333+/27lzp1u0aJFLSEhwy5cvb+qP2CRiUafPPvvMSXJr1qypsl5ZWVl4mZZWp127drmMjAz3wAMPuPfff9/t3r3bvfrqq+7zzz+vdZvFxcWuS5cu7kc/+pHbsWOHe+GFF1xqaqp77LHHwsv4VifnYlOrr7/+2t19993umWeecXl5eW7s2LE1lvGtVrGo09y5c91DDz3k3n77bbdr1y73xBNPuFatWrmXX345vAx1cm737t1uyZIlbtu2bW7Pnj3upZdecp07d3YzZswIL0OdKrSkc55zsanV2Xjeq/w5i4qK3JIlS1xcXJzbvXt3rdtsiee9WNTpTD3nEWqayWWXXebuvvvuKtN69+7tpk+f7pyzznqkgyQa06ZNcxMmTHC///3va/wPftq0aa53795Vpt11111u6NChDdpXrMWiTqH/uW/durXWZVpanW699VY3YcKEem1z4cKFLi0tzX377bfhafPmzXPdunVz5eXlzjn/6uRcbGpVWW3HpG+1inWdQm644QZ3xx13hN9Tp8imTp3qvv/974ffU6cKLemc51xsanU2nveqGzt2rBs5cmSd22yJ571Y1KmyM+mcx+VnzeD48ePasmWLRo0aVWX6qFGjtGnTpvD7devWqXPnzrrwwguVn5+vL774osrykyZN0ogRI6pMe+utt/T888/rqaeeirjvzZs319hvXl6e3nvvvRqXNjS3WNZJksaMGaPOnTvryiuv1PLly6vMa0l1Ki8v12uvvaYLL7xQeXl56ty5s4YMGaIXX3yxyvLV67R582YNHz68yh/bysvL04EDB7Rnz57wMr7USYpdraLhU61OZ52Ki4uVkZERfk+datq1a5def/11DR8+PDyNOpmWdM6TYn9MnS3nveo+//xzvfbaa7rzzjurTG/p571Y1SkazVEnQk0z+Oqrr3Ty5El16dKlyvQuXbro4MGDkqTrr79ezz33nN566y39+te/1rvvvquRI0eqrKwsvHzXrl2VlZUVfn/o0CFNmjRJBQUFat++fcR9Hzx4MOJ+T5w4oa+++qqpPmKTiFWd2rVrpwULFmj58uX661//qmuuuUa33nqrnn322fAyLalOX3zxhb7++ms9+uijGj16tFatWqVbbrlF48aN0/r168PLV69TbTUIzatrmTOxTlLsahUNn2p1uuq0fPlyvfvuu7rjjjvC06hThSuuuELJycm64IILNGzYMM2ZMyc8jzq1vHOeFLtanW3nver+8Ic/KDU1VePGjasyvaWf92JVp2g0R53iY7JVRCUuLq7Ke+dceNqtt94ant6vXz8NHjxY2dnZeu2118IH27x586qsn5+frx//+Me66qqr6r3fSNPPFE1dp44dO2rq1Knh94MHD9bhw4f1q1/9ShMmTKhzv5Gmnylqq1N5ebkkaezYseHPPWDAAG3atElPP/10+Nvf6nWqbZvVp/tWJyk2tWrofiNNP1PEsk7r1q3TpEmTtGjRIvXt2/eU+400/UwRqzotW7ZMpaWl+uCDD/TAAw/oscce07Rp0+rcb6TpZ4qmrlNLPedJTV+rs+28V92SJUt0++23Kzk5ucr0s+W8F4s6NXS/kaY3FUZqmkHHjh3VunXrGin5iy++qJFqQ7p27ars7Gx98skntW73rbfe0mOPPab4+HjFx8frzjvvVHFxseLj47VkyRJJUmZmZsT9xsfHq0OHDo38ZE0rVnWKZOjQoVXWaUl16tixo+Lj49WnT58q8y+66KI6n5ZTWw2kim+ufKqTFLtaRcOnWsW6TuvXr9fNN9+sBQsW6Kc//WmVedSpQo8ePdSnTx/ddtttevTRRzVr1iydPHlSEnWSWt45Tzq9/49qyee9yjZu3KjCwkL97Gc/O+V2W9p5L1Z1ikZz1IlQ0wwSExOVm5ur1atXV5m+evVqXXHFFRHXOXTokPbv36+uXbvWut3Nmzdr27Zt4decOXOUmpqqbdu26ZZbbpEkXX755TX2u2rVKg0ePFgJCQmN/GRNK1Z1imTr1q1V1mlJdUpMTNSll16qwsLCKvP/9a9/KTs7u9btXn755dqwYYOOHz8enrZq1Sp169ZN5513XngZX+okxa5W0fCpVrGs07p163TjjTfq0Ucf1eTJk2vMp06ROef03Xffhb/ppE4t75wnnd5jqiWf9ypbvHixcnNz1b9//1Nut6Wd92JVp2g0S51i9ggC1Cn0iL3Fixe7nTt3uilTpri2bdu6PXv2uNLSUvfzn//cbdq0yX322Wdu7dq17vLLL3fdu3d3JSUl4W1Mnz7d/eQnP6l1H5GeBBN6xN7UqVPdzp073eLFi714FGFT1qmgoMA999xzbufOne6f//ynmz9/vktISHALFiwIL9OS6uSccytWrHAJCQnumWeecZ988on7zW9+41q3bu02btwY3kb1Oh05csR16dLF3XbbbW7Hjh1uxYoVrn379hEfbelLnZyLTa2cc+6jjz5yW7dudTfffLMbMWKE27p1a5UnDflWq1jUae3ata5NmzZuxowZtT6GnTo59+yzz7ply5a5nTt3ut27d7u//OUvrnv37u72228PL0OdamoJ5zznYlOrs/G855w9orlNmzbut7/9bcRtnA3nvVjUybkz85xHqGlGTz31lMvOznaJiYlu0KBBbv369c45544ePepGjRrlOnXq5BISElxWVpabOHGi27dvX5X1J06c6IYPH17r9iP9D94559atW+cGDhzoEhMT3XnnnVfrQXymaOo6FRQUuIsuusi1adPGpaamutzcXPenP/2pxn5bSp1CFi9e7Hr16uWSk5Nd//793YsvvlhlfqTjafv27W7YsGEuKSnJZWZmulmzZoUfaxniW52ci02tsrOznaQar8p8q1VT12nixIkRa1S9lmd7nZYuXeoGDRrk2rVr59q2bev69OnjfvnLX7pjx45VWe9sr1N1LeWc51zT1+psPe/97ne/cykpKe7IkSMR1z9bznuxqNOZeM6Lc67an0kFAAAAAI9wTw0AAAAArxFqAAAAAHiNUAMAAADAa4QaAAAAAF4j1AAAAADwGqEGAAAAgNcINQAAAAC8RqgBAAAA4DVCDQAAAACvEWoAAAAAeI1QAwAAAMBrhBoAAAAAXvv/ARzsCjc6tLQAAAAASUVORK5CYII=", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# QC Plot\n", + "try:\n", + " qc_variable = df_filter_3.var_name.values[0]\n", + " print(qc_variable)\n", + "except Exception as e:\n", + " print(e)\n", + " \n", + "try:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + " qc_ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "d76e4d27", + "metadata": {}, + "source": [ + "#### bonus: choose variables to plot from a dropdown menu " + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "id": "bb733804", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
var_namedimsis_dimn_dimattrsdtype
4pres(time,)False1{'long_name': 'Barometric pressure', 'units': ...float32
6tdry(time,)False1{'long_name': 'Dry bulb temperature', 'units':...float32
8dp(time,)False1{'long_name': 'Dewpoint temperature', 'units':...float32
10wspd(time,)False1{'long_name': 'Wind speed', 'units': 'm/s', 'v...float32
12deg(time,)False1{'long_name': 'Wind direction', 'units': 'deg'...float32
14rh(time,)False1{'long_name': 'Relative humidity', 'units': '%...float32
16u_wind(time,)False1{'long_name': 'Eastward wind component', 'unit...float32
18v_wind(time,)False1{'long_name': 'Northward wind component', 'uni...float32
20wstat(time,)False1{'long_name': 'Wind status', 'units': 'unitless'}float32
21asc(time,)False1{'long_name': 'Ascent rate', 'units': 'm/s', '...float32
23rh_smooth(time,)False1{'long_name': 'Smoothed original relative humi...float32
25rh_biased(time,)False1{'long_name': 'Dry bias corrected relative hum...float32
27rh_adjust(time,)False1{'long_name': 'Final corrected ambient relativ...float32
29rh_scaled(time,)False1{'long_name': 'Scaled final corrected ambient ...float32
31dp_scaled(time,)False1{'long_name': 'Scaled dewpoint temperature', '...float32
\n", + "
" + ], + "text/plain": [ + " var_name dims is_dim n_dim \\\n", + "4 pres (time,) False 1 \n", + "6 tdry (time,) False 1 \n", + "8 dp (time,) False 1 \n", + "10 wspd (time,) False 1 \n", + "12 deg (time,) False 1 \n", + "14 rh (time,) False 1 \n", + "16 u_wind (time,) False 1 \n", + "18 v_wind (time,) False 1 \n", + "20 wstat (time,) False 1 \n", + "21 asc (time,) False 1 \n", + "23 rh_smooth (time,) False 1 \n", + "25 rh_biased (time,) False 1 \n", + "27 rh_adjust (time,) False 1 \n", + "29 rh_scaled (time,) False 1 \n", + "31 dp_scaled (time,) False 1 \n", + "\n", + " attrs dtype \n", + "4 {'long_name': 'Barometric pressure', 'units': ... float32 \n", + "6 {'long_name': 'Dry bulb temperature', 'units':... float32 \n", + "8 {'long_name': 'Dewpoint temperature', 'units':... float32 \n", + "10 {'long_name': 'Wind speed', 'units': 'm/s', 'v... float32 \n", + "12 {'long_name': 'Wind direction', 'units': 'deg'... float32 \n", + "14 {'long_name': 'Relative humidity', 'units': '%... float32 \n", + "16 {'long_name': 'Eastward wind component', 'unit... float32 \n", + "18 {'long_name': 'Northward wind component', 'uni... float32 \n", + "20 {'long_name': 'Wind status', 'units': 'unitless'} float32 \n", + "21 {'long_name': 'Ascent rate', 'units': 'm/s', '... float32 \n", + "23 {'long_name': 'Smoothed original relative humi... float32 \n", + "25 {'long_name': 'Dry bias corrected relative hum... float32 \n", + "27 {'long_name': 'Final corrected ambient relativ... float32 \n", + "29 {'long_name': 'Scaled final corrected ambient ... float32 \n", + "31 {'long_name': 'Scaled dewpoint temperature', '... float32 " + ] + }, + "execution_count": 32, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Valid variables filtering\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_4 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_4" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "id": "82d5c20d", + "metadata": {}, + "outputs": [], + "source": [ + "# example 1: using xarray plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "id": "2fc9aaa4", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# fig, ax = plt.subplots(figsize=(10, 4))\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "# fig.clear() # Remove old lines from plot and plot new one\n", + "# if len(ds[var].dims)==2:\n", + "# ds[var].plot(x=\"time\", add_colorbar=False)\n", + "# else:\n", + "# ds[var].plot()\n", + "# plt.grid()\n", + "# plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "id": "fabdd802", + "metadata": {}, + "outputs": [], + "source": [ + "# example 2: using act plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "id": "962b4186", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "\n", + "\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "\n", + "# i_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + "# i_display.add_subplots((1,), figsize=(10, 4))\n", + "# ax = i_display.plot(var, subplot_index=(0,), set_title=f\"{var} ({ds_act[var].attrs['long_name']})\",)\n", + "\n", + "# ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "# ax.grid()\n", + "# plt.show()\n" + ] + }, + { + "cell_type": "markdown", + "id": "ba714aa6", + "metadata": {}, + "source": [ + "## Skew-T Plot" + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "id": "606dc551", + "metadata": {}, + "outputs": [ + { + "ename": "NameError", + "evalue": "name 'files_list' is not defined", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[0;32mIn[37], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m launch_times \u001b[38;5;241m=\u001b[39m [\u001b[38;5;28mstr\u001b[39m(datetime\u001b[38;5;241m.\u001b[39mstrptime(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;241m.\u001b[39mjoin(f\u001b[38;5;241m.\u001b[39msplit(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m.\u001b[39m\u001b[38;5;124m'\u001b[39m)[\u001b[38;5;241m-\u001b[39m\u001b[38;5;241m3\u001b[39m:\u001b[38;5;241m-\u001b[39m\u001b[38;5;241m1\u001b[39m]), \u001b[38;5;124m'\u001b[39m\u001b[38;5;124m%\u001b[39m\u001b[38;5;124mY\u001b[39m\u001b[38;5;124m%\u001b[39m\u001b[38;5;124mm\u001b[39m\u001b[38;5;132;01m%d\u001b[39;00m\u001b[38;5;124m%\u001b[39m\u001b[38;5;124mH\u001b[39m\u001b[38;5;124m%\u001b[39m\u001b[38;5;124mM\u001b[39m\u001b[38;5;124m%\u001b[39m\u001b[38;5;124mS\u001b[39m\u001b[38;5;124m'\u001b[39m)) \u001b[38;5;28;01mfor\u001b[39;00m f \u001b[38;5;129;01min\u001b[39;00m \u001b[43mfiles_list\u001b[49m]\n\u001b[1;32m 2\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mAvailable sonde launch times:\u001b[39m\u001b[38;5;124m'\u001b[39m)\n\u001b[1;32m 3\u001b[0m display(pd\u001b[38;5;241m.\u001b[39mDataFrame(launch_times, columns\u001b[38;5;241m=\u001b[39m[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mLaunch Time\u001b[39m\u001b[38;5;124m'\u001b[39m]))\n", + "\u001b[0;31mNameError\u001b[0m: name 'files_list' is not defined" + ] + } + ], + "source": [ + "launch_times = [str(datetime.strptime(''.join(f.split('.')[-3:-1]), '%Y%m%d%H%M%S')) for f in files_list]\n", + "print('Available sonde launch times:')\n", + "display(pd.DataFrame(launch_times, columns=['Launch Time']))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d85d73a9", + "metadata": {}, + "outputs": [], + "source": [ + "# select sonde launch time from the list\n", + "launch_time_index = 0\n", + "sonde_file = files_list[launch_time_index]\n", + "sonde_ds = act.io.armfiles.read_netcdf(sonde_file)\n", + "\n", + "# Calculate stability indicies\n", + "sonde_ds = act.retrievals.calculate_stability_indicies(\n", + " sonde_ds, temp_name='tdry', td_name='dp', p_name='pres', rh_name='rh'\n", + ")\n", + "\n", + "# Set up plot\n", + "skewt = act.plotting.SkewTDisplay(sonde_ds, figsize=(7, 10))\n", + "\n", + "# Add data\n", + "skewt.plot_from_u_and_v('u_wind', 'v_wind', 'pres', 'tdry', 'dp', set_title=f'Skew-T Plot for {launch_times[launch_time_index]}')\n", + "sonde_ds.close()\n", + "plt.show()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/SONDEADJUST/SONDEADJUST_tutorial.ipynb b/VAPs/quicklook/SONDEADJUST/SONDEADJUST_tutorial.ipynb new file mode 100644 index 00000000..b964d6ae --- /dev/null +++ b/VAPs/quicklook/SONDEADJUST/SONDEADJUST_tutorial.ipynb @@ -0,0 +1,884 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# SONDEADJUST.C1 Notebook\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/sondeadjust) for more information about this vap." + ] + }, + { + "cell_type": "markdown", + "id": "97097763", + "metadata": {}, + "source": [ + "In this notebook, we demonstrate the workflow to explore ARM vap data (using sondeadjust as an example.) Value-added products (VAPs) are higher-order data products that have been analyzed and processed to ease scientists’ use of ARM data in atmospheric research and global climate models. \n", + "Here is the main content we will cover." + ] + }, + { + "cell_type": "markdown", + "id": "eddec40f", + "metadata": {}, + "source": [ + "# Table of Content\n", + "## Access the data\n", + "* How to retrieve the data\n", + "* Data path and file name conventions\n", + "* Load data\n", + "## Explore the data\n", + "* NetCDF Data structure\n", + "* Xarray essentials\n", + "* Xarray Variable\n", + "## Plot the data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "import random\n", + "\n", + "import glob\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "2372d5be", + "metadata": {}, + "source": [ + "## Access the data" + ] + }, + { + "cell_type": "markdown", + "id": "5b0d9684", + "metadata": {}, + "source": [ + "### How to retrieve the data\n", + "We assume the path \"/data/archive\" is available where you are running this notebook. You can use `os.path.exists(\"/data/archive\")` to verify if the path exists at your machine.\n", + "\n", + "\n", + "### Data path and file name conventions\n", + "There several common terminologies regarding ARM data, for example, data-stream-name, data-level, site, facility, instrument, etc. (For more details, please see [ARM Data File Standards Version 1.2](https://www.google.com/search?q=arm+datastream+facility+cite+definition&rlz=1C1GCEJ_enUS1029US1029&ei=hb41ZICrBPukqtsPvMODIA&ved=0ahUKEwjAgeiS0aL-AhV7kmoFHbzhAAQQ4dUDCBA&uact=5&oq=arm+datastream+facility+cite+definition&gs_lcp=Cgxnd3Mtd2l6LXNlcnAQAzIFCAAQogQyBQgAEKIEMgUIABCiBDIFCAAQogQyBQgAEKIEOgoIABBHENYEELADOgoIIRCgARDDBBAKSgQIQRgAUJ4DWIoOYO0PaAFwAXgAgAGMAYgBuQmSAQMyLjmYAQCgAQHIAQjAAQE&sclient=gws-wiz-serp).) \n", + "\n", + "For example, this notebook is called `sondeadjust.c1`, where `sondeadjust` is the \"datastream name\", and the `{process.ds_class_level}}` is the \"data level\".\n", + "\n", + "This datastream also contains site `fkb` and facility `M1`. (Note: individual datastream might have multiple site-facility pairs.)\n", + "In such a case, the data of this data-stream is stored at `/data/archive/fkb/fkbsondeadjustM1.c1`, which is in the format of `//.`. We can use the following method to assign the data-stream directory `datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )`\n", + "\n", + "The data files under datastream_dir also follows naming conventions. But once reach the datastream_dir level, the most import file naming convention to differentiate the files is \"yyyyMMdd.hhmmss\", which comes handy to filter out files based on datetime. For example, we can use `glob.glob(f'{datastream_dir}/*.200709*.*')` to filter files in 2007 September.\n", + "\n", + "(Note: refer to the https://adc.arm.gov/discovery/#/ and https://adc.arm.gov/solr8/metadata/select API to explore ARM datastream and assoicated available sites and facilities\n", + "\n", + "Please see the following examples in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d7e9eb85", + "metadata": {}, + "outputs": [], + "source": [ + "# Verify if DATA_DIR path exists\n", + "DATA_DIR = \"/data/archive\"\n", + "os.path.exists(DATA_DIR)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "586993fd", + "metadata": {}, + "outputs": [], + "source": [ + "# Speicify datastream_dir following the path conventions and check its existence\n", + "DATASTREAM_NAME = \"sondeadjust\"\n", + "DATA_LEVEL = \"c1\"\n", + "site = \"fkb\"\n", + "facility = \"M1\"\n", + "datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )\n", + "print(datastream_dir)\n", + "print(os.path.exists(datastream_dir))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0742f7c1", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: list 5 (random) files under datastream_dir\n", + "files = os.listdir(datastream_dir)\n", + "files[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39b98a36", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: get most recent file\n", + "list_of_files = glob.glob(f\"{datastream_dir}/*\") # * means all if need specific format then *.csv\n", + "latest_file = max(list_of_files, key=os.path.getctime)\n", + "latest_file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "902d514e", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus sort datastream files based on datetime\n", + "files = os.listdir(datastream_dir)\n", + "file_sorted = files.copy()\n", + "file_sorted.sort() \n", + "print(file_sorted[:5])\n", + "\n", + "# to reverse\n", + "file_sorted_reverse = files.copy() \n", + "file_sorted_reverse.sort(reverse=True)\n", + "print(file_sorted_reverse[:5])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec5923b2", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: pattern matching\n", + "# filter the 200709** files under datastream_dir\n", + "files_filter = glob.glob(f'{datastream_dir}/*.200709*.*')\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c4a4aa18", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: use armnotebook_utils.file_filter (TODO) to filter files based on datastream info \n" + ] + }, + { + "cell_type": "markdown", + "id": "cfeb9efc", + "metadata": {}, + "source": [ + "### Load data\n", + "The arm data is stored in [NetCDF](https://en.wikipedia.org/wiki/NetCDF#:~:text=%22NetCDF%20(network%20Common%20Data%20Form,format%20for%20representing%20scientific%20data.) format. We can use xarray's `open_dataset` method to load single file, or `open_mfdataset` to open multiple files. (Note: the latter will still return a single xarray dataset object by combining multiple files.)\n", + "\n", + "Note: open_dataset keeps the file handle open and lazy loads its contents. All parameters are passed directly to open_dataset. It is a preferable method over load_dataset for memory-effiency. (more details, please see [xarray.open_dataset](https://docs.xarray.dev/en/stable/generated/xarray.open_dataset.html))\n", + "\n", + "See the following example in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a440a329", + "metadata": {}, + "outputs": [], + "source": [ + "# open single file\n", + "full_path = latest_file\n", + "print(full_path)\n", + "ds_single = xr.open_dataset(full_path)\n", + "ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0143a3d", + "metadata": {}, + "outputs": [], + "source": [ + "print(ds_single)\n", + "print(type(ds_single))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c0f8939", + "metadata": {}, + "outputs": [], + "source": [ + "# open multiple files\n", + "n_files = 3\n", + "full_paths = [os.path.join(datastream_dir, f_path) for f_path in file_sorted_reverse[:n_files]]\n", + "print(full_paths)\n", + "try: # Note: sometimes multiple files cannot be merged, so we used try except here.\n", + " ds_mutiple = xr.open_mfdataset(full_paths)\n", + " # ds_mutiple\n", + " print(type(ds_mutiple))\n", + " print(ds_mutiple)\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "3fa59943", + "metadata": {}, + "source": [ + "## Explore the data" + ] + }, + { + "cell_type": "markdown", + "id": "571f69b9", + "metadata": {}, + "source": [ + "### NetCDF Data structure \n", + "\n", + "\n", + "(If you are confident with NetCDF basics and xarray essentials, feel free to skip this session.)\n", + "Before we dive into data exploration, there are some eseential concpets we should be familiar with (shown below): \n", + "* Dataset\n", + "* Data array\n", + "* Variable\n", + "* Dimenssion\n", + "* Coordinate\n", + "* Data Type\n", + "* Meta Data (Attributes)\n", + "\n", + "We will not go into details about NetCDF basics and here are some references you might find helpful\n", + "* [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#)\n", + "* [Network Common Data Form (NetCDF)](https://www.unidata.ucar.edu/software/netcdf/) and [A Brief History of (netCDF) Time](https://www.unidata.ucar.edu/software/netcdf/time/recs.html)\n", + "\n", + "\n", + "\n", + "### Xarray essentials\n", + "Earlier we introduced the xarray pacakge and used `open_dataset` and `open_mfdataset` to retrieve NetCDF data file as an xarray Dataset object. Recall that you can review the data in a notebook by using `print(ds)` or `ds`. \n", + "\n", + "Using xarray to retrieve the aforementioned NetCDF basics is straightforwared. In general,\n", + "* Dataset: `ds`\n", + "* Data array: `ds.variables`\n", + "* Variable: `ds.ds.variables`\n", + "* Dimenssion: `ds.dims`\n", + "* Coordinate: `ds.coords`\n", + "* Data Type: `type`\n", + "* Meta Data (Attributes)\n", + "\n", + "Also, here are some references if you are new to xarray\n", + "* [Xarray in 45 minutes](https://tutorial.xarray.dev/overview/xarray-in-45-min.html)\n", + "* [Handling NetCDF Files using XArray for Absolute Beginners](https://towardsdatascience.com/handling-netcdf-files-using-xarray-for-absolute-beginners-111a8ab4463f)\n", + "\n", + "Try the following commands in action\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b3eac323", + "metadata": {}, + "outputs": [], + "source": [ + "ds = ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77ecf85d", + "metadata": {}, + "outputs": [], + "source": [ + "# Dataset \n", + "ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25e7de09", + "metadata": {}, + "outputs": [], + "source": [ + "# Data array, Variable\n", + "# Note: the info can be overwhelming. Do not attempt to grasp everything at the first glance.\n", + "ds.variables" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c41b67e", + "metadata": {}, + "outputs": [], + "source": [ + "# dimenssions\n", + "ds.dims" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "156f1dfc", + "metadata": {}, + "outputs": [], + "source": [ + "# coordinates\n", + "ds.coords" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "277d6064", + "metadata": {}, + "outputs": [], + "source": [ + "# Meta Data (Attributes)\n", + "ds.attrs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d334681f", + "metadata": {}, + "outputs": [], + "source": [ + "# type\n", + "print(type(ds))\n", + "print(type(ds.variables))\n", + "print(type(ds.dims))\n", + "print(type(ds.coords))\n", + "print(type(ds.attrs))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "643399d6", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: retrieve variable names only\n", + "list(ds.variables)" + ] + }, + { + "cell_type": "markdown", + "id": "1cf36878", + "metadata": {}, + "source": [ + "#### Discussion: variable vs. coordinates vs. dimenssions. \n", + "From [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#):\n", + "\n", + "* Dimession: A dimension may be used to represent a real physical dimension, for example, time, latitude, longitude, or height. A dimension might also be used to index other quantities, for example station or model-run-number.\n", + "A netCDF dimension has both a name and a length. A dimension length is an arbitrary positive integer, except that one dimension in a netCDF dataset can have the length UNLIMITED.\n", + "\n", + "* Variables: Variables are used to store the bulk of the data in a netCDF dataset. A variable represents an array of values of the same type. A scalar value is treated as a 0-dimensional array. A variable has a name, a data type, and a shape described by its list of dimensions specified when the variable is created. A variable may also have associated attributes, which may be added, deleted or changed after the variable is created.\n", + "\n", + "* Coordinate (Variables): It is legal for a variable to have the same name as a dimension. Such variables have no special meaning to the netCDF library. However there is a convention that such variables should be treated in a special way by software using this library.\n", + "A variable with the same name as a dimension is called a coordinate variable. It typically defines a physical coordinate corresponding to that dimension.\n", + "\n", + "\n", + "Tips: By definition, coordinates and dimenssions are also variable. For example ds.time is a coordinate but it is also a special variable. But in practice, when we talk about variable, it implies regular/non-coordinate variable. We will use this convention for the remaining notebook. " + ] + }, + { + "cell_type": "markdown", + "id": "d47120f9", + "metadata": {}, + "source": [ + "### Xarray Variable\n", + "\n", + "\n", + "We can use `ds.variables` to access varialbes of a dataset. But many times viewing print-out the whole datasets can be overwhemling. Instead, we would work on the individual variable (also called Xarray data array.) using the `ds[var_name]` syntax, where `var_name` is the name of the variable. See the following examples in action.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5a57e136", + "metadata": {}, + "outputs": [], + "source": [ + "print(type(ds[\"time\"]))\n", + "ds[\"time\"]" + ] + }, + { + "cell_type": "markdown", + "id": "51ec643e", + "metadata": {}, + "source": [ + "#### Variable properties\n", + "For individual varible, the following properties are mostly used with the assocaited xarray method to retrieve them. (assuming var = ds[var_name])\n", + "* name: `var.name`\n", + "* data content: `var.data`\n", + "* attributes: `var.attrs`\n", + "* dimenstions: `var.dims`\n", + "* data type: `var.data.dtype`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a632a525", + "metadata": {}, + "outputs": [], + "source": [ + "var_name = \"time\"\n", + "var = ds[var_name]\n", + "\n", + "print(\"var.name: \\n\", var.name, \"\\n\")\n", + "print(\"var.data: \\n\", var.data, \"\\n\")\n", + "print(\"var.attrs: \\n\", var.attrs, \"\\n\")\n", + "print(\"var.dims: \\n\", var.dims, \"\\n\")\n", + "print(\"var.data.dtype: \\n\", var.data.dtype, \"\\n\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b256b07f", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: indivdual variable info table (collect and display variable info as a tabular format)\n", + "df_info = pd.DataFrame()\n", + "df_info[\"var_name\"] = list(ds.variables)\n", + "df_info[\"dims\"] = df_info.var_name.apply(lambda x: ds[x].dims)\n", + "df_info[\"is_dim\"] = df_info.var_name.apply(lambda x: x in ds.dims)\n", + "df_info[\"n_dim\"] = df_info.var_name.apply(lambda x: len(ds[x].dims))\n", + "df_info[\"attrs\"] = df_info.var_name.apply(lambda x: ds[x].attrs)\n", + "df_info[\"dtype\"] = df_info.var_name.apply(lambda x: ds[x].data.dtype)\n", + "df_info" + ] + }, + { + "cell_type": "markdown", + "id": "e6a36b96", + "metadata": {}, + "source": [ + "### Data cleaning/Preprocessing (skipped)\n", + "Data cleaning and preprocessing is an important stage in the the data analysis pipeline. However it will be out of the scope of this notebook. For more information about data cleaning and preprocessing basics, here are some references. \n", + "* [Xarray Fundamentals](https://earth-env-data-science.github.io/lectures/xarray/xarray.html)\n", + "* [Xarray Tutorial — Pangeo Gallery documentation](http://gallery.pangeo.io/repos/pangeo-data/pangeo-tutorial-gallery/xarray.html)\n", + "* [Pythonic Data Cleaning With pandas and NumPy](https://realpython.com/python-data-cleaning-numpy-pandas/)\n", + "* [Pandas - Cleaning Data](https://www.w3schools.com/python/pandas/pandas_cleaning.asp)" + ] + }, + { + "cell_type": "markdown", + "id": "ae6e1a69", + "metadata": {}, + "source": [ + "### Plotting\n", + "Data visualization (or plotting) is another important data analysis topic and deserves its own discussion. Here in this notebook we will only demonstrate basic tool for simple data visualization tasks.\n", + "\n", + "Here are some reference you might find useful:\n", + "* xarray plotting: https://docs.xarray.dev/en/stable/user-guide/plotting.html\n", + "* Atmospheric Community Toolkit (ACT): https://arm-doe.github.io/ACT/index.html\n", + "\n", + "Note: this notebook is auto-generated using a template. It uses a general idea to select variable(s) to plot and is not customized to each indivdual notebook. Feel free to change the variables in intrrests, especially certain figure is failed to plot." + ] + }, + { + "cell_type": "markdown", + "id": "7e417fb2", + "metadata": {}, + "source": [ + "#### 1-dimenssional basic time series plot\n", + "\n", + "For the following plot we would like to find variables such that\n", + "* it has one and only one dimession\n", + "* \"time\" is its coordinate variable\n", + "* it is not a dimenssion itself,\n", + "* it is not a special variable with substrings within [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "\n", + "Please see the following example in action. (For more details about pandas filtering, please see the following references.)\n", + "* [pandas: multiple conditions while indexing data frame](https://stackoverflow.com/questions/22591174/pandas-multiple-conditions-while-indexing-data-frame-unexpected-behavior)\n", + "* [pandas.DataFrame.apply](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.apply.html)\n", + "* [How to test if a string contains one of the substrings in a list...](https://stackoverflow.com/questions/26577516/how-to-test-if-a-string-contains-one-of-the-substrings-in-a-list-in-pandas" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30edac2d", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==1, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter = df_info[(df_info.n_dim == 1) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4777a659", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_1d = df_filter.var_name.values[0]\n", + " var_1d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb5ae985", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " ds[var_1d].plot()\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "9e41066e", + "metadata": {}, + "source": [ + "#### 2-dimenssional basic plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09bd4adc", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==2, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_2 = df_info[(df_info.n_dim == 2) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2cdea1a9", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_2d = df_filter_2.var_name.values[0]\n", + " var_2d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5f48879", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " print(ds[var_2d].dims)\n", + " # ds[var_2d].plot()\n", + "\n", + " # conventionally, use \"time\" as x-axis\n", + " ds[var_2d].plot(x=\"time\")\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "7554b2a6", + "metadata": {}, + "source": [ + "#### qc-plotting (optional)\n", + "\n", + "Note: act qc plotting has more strict requirement, one of them is the associated qc variable need to have \"flag_masks\" attributes. Which is added by using ds = act.io.armfiles.read_netcdf(files_list), then ds.clean.cleanup()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71c2096f", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " ds_act = act.io.armfiles.read_netcdf(full_path)\n", + " print(type(ds_act))\n", + " ds_act.clean.cleanup()\n", + "\n", + " # or \n", + " # ds.clean.cleanup()\n", + "except Exception as e:\n", + " print(\"ERROR\", e)\n", + " ds_act = ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "844f8505", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter valid variables for ACT qc plotting\n", + "condition = (\"qc_\" + df_info.var_name).apply(lambda x: ds_act[x].attrs.get(\"flag_masks\") is not None \n", + " if x in list(ds.variables) else False)\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_3 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings))) &\n", + " condition\n", + " ]\n", + "df_filter_3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd38cc6", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "try:\n", + " qc_variable = df_filter_3.var_name.values[0]\n", + " print(qc_variable)\n", + "except Exception as e:\n", + " print(e)\n", + " \n", + "try:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + " qc_ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "d76e4d27", + "metadata": {}, + "source": [ + "#### bonus: choose variables to plot from a dropdown menu " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb733804", + "metadata": {}, + "outputs": [], + "source": [ + "# Valid variables filtering\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_4 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82d5c20d", + "metadata": {}, + "outputs": [], + "source": [ + "# example 1: using xarray plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2fc9aaa4", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# fig, ax = plt.subplots(figsize=(10, 4))\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "# fig.clear() # Remove old lines from plot and plot new one\n", + "# if len(ds[var].dims)==2:\n", + "# ds[var].plot(x=\"time\", add_colorbar=False)\n", + "# else:\n", + "# ds[var].plot()\n", + "# plt.grid()\n", + "# plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fabdd802", + "metadata": {}, + "outputs": [], + "source": [ + "# example 2: using act plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "962b4186", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "\n", + "\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "\n", + "# i_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + "# i_display.add_subplots((1,), figsize=(10, 4))\n", + "# ax = i_display.plot(var, subplot_index=(0,), set_title=f\"{var} ({ds_act[var].attrs['long_name']})\",)\n", + "\n", + "# ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "# ax.grid()\n", + "# plt.show()\n" + ] + }, + { + "cell_type": "markdown", + "id": "10d523d7", + "metadata": {}, + "source": [ + "## Skew-T Plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9133a183", + "metadata": {}, + "outputs": [], + "source": [ + "launch_times = [str(datetime.strptime(''.join(f.split('.')[-3:-1]), '%Y%m%d%H%M%S')) for f in files_list]\n", + "print('Available sonde launch times:')\n", + "display(pd.DataFrame(launch_times, columns=['Launch Time']))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b3af3f5a", + "metadata": {}, + "outputs": [], + "source": [ + "# select sonde launch time from the list\n", + "launch_time_index = 0\n", + "sonde_file = files_list[launch_time_index]\n", + "sonde_ds = act.io.armfiles.read_netcdf(sonde_file)\n", + "\n", + "# Calculate stability indicies\n", + "sonde_ds = act.retrievals.calculate_stability_indicies(\n", + " sonde_ds, temp_name='tdry', td_name='dp', p_name='pres', rh_name='rh'\n", + ")\n", + "\n", + "# Set up plot\n", + "skewt = act.plotting.SkewTDisplay(sonde_ds, figsize=(7, 10))\n", + "\n", + "# Add data\n", + "skewt.plot_from_u_and_v('u_wind', 'v_wind', 'pres', 'tdry', 'dp', set_title=f'Skew-T Plot for {launch_times[launch_time_index]}')\n", + "sonde_ds.close()\n", + "plt.show()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/SONDEADJUST/sondeadjust.c1.ipynb b/VAPs/quicklook/SONDEADJUST/sondeadjust.c1.ipynb new file mode 100644 index 00000000..2cda801c --- /dev/null +++ b/VAPs/quicklook/SONDEADJUST/sondeadjust.c1.ipynb @@ -0,0 +1,385 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# SONDEADJUST.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/sondeadjust) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'sondeadjust'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2008-01-01', 'facility': 'M1', 'site': 'fkb', 'start_date': '2007-03-24'}, {'end_date': '2008-12-28', 'facility': 'M1', 'site': 'hfe', 'start_date': '2008-05-14'}, {'end_date': '2012-02-09', 'facility': 'M1', 'site': 'gan', 'start_date': '2011-09-17'}, {'end_date': '2011-01-05', 'facility': 'M1', 'site': 'grw', 'start_date': '2009-04-16'}, {'end_date': '2013-06-29', 'facility': 'M1', 'site': 'pvc', 'start_date': '2012-06-25'}, {'end_date': '2005-09-15', 'facility': 'M1', 'site': 'pye', 'start_date': '2005-02-25'}, {'end_date': '2012-03-31', 'facility': 'M1', 'site': 'pgh', 'start_date': '2011-06-15'}, {'end_date': '2012-07-16', 'facility': 'C1', 'site': 'nsa', 'start_date': '2002-04-28'}, {'end_date': '2007-01-08', 'facility': 'M1', 'site': 'nim', 'start_date': '2006-01-07'}, {'end_date': '2011-04-24', 'facility': 'M1', 'site': 'sbs', 'start_date': '2010-11-08'}, {'end_date': '2012-09-01', 'facility': 'C1', 'site': 'sgp', 'start_date': '1999-07-20'}, {'end_date': '2011-09-09', 'facility': 'C1', 'site': 'twp', 'start_date': '1997-08-28'}, {'end_date': '2012-04-05', 'facility': 'C2', 'site': 'twp', 'start_date': '1998-11-05'}, {'end_date': '2012-12-19', 'facility': 'C3', 'site': 'twp', 'start_date': '2002-04-01'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2012-08-30'\n", + "date_end = '2012-08-31'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['pres', 'tdry', 'dp']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],linestyle='None')\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'pres'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,linestyle='None')\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'pres'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],linestyle='None')\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],linestyle='None')\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + }, + { + "cell_type": "markdown", + "id": "bd86dac3", + "metadata": {}, + "source": [ + "## Skew-T Plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c23b6c56", + "metadata": {}, + "outputs": [], + "source": [ + "launch_times = [str(datetime.strptime(''.join(f.split('.')[-3:-1]), '%Y%m%d%H%M%S')) for f in files_list]\n", + "print('Available sonde launch times:')\n", + "display(pd.DataFrame(launch_times, columns=['Launch Time']))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c958b473", + "metadata": {}, + "outputs": [], + "source": [ + "# select sonde launch time from the list\n", + "launch_time_index = 0\n", + "sonde_file = files_list[launch_time_index]\n", + "sonde_ds = act.io.armfiles.read_netcdf(sonde_file)\n", + "\n", + "# Calculate stability indicies\n", + "sonde_ds = act.retrievals.calculate_stability_indicies(\n", + " sonde_ds, temp_name='tdry', td_name='dp', p_name='pres', rh_name='rh'\n", + ")\n", + "\n", + "# Set up plot\n", + "skewt = act.plotting.SkewTDisplay(sonde_ds, figsize=(7, 10))\n", + "\n", + "# Add data\n", + "skewt.plot_from_u_and_v('u_wind', 'v_wind', 'pres', 'tdry', 'dp', set_title=f'Skew-T Plot for {launch_times[launch_time_index]}')\n", + "sonde_ds.close()\n", + "plt.show()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/SONDEPARAM/SONDEPARAM_tutorial.ipynb b/VAPs/quicklook/SONDEPARAM/SONDEPARAM_tutorial.ipynb new file mode 100644 index 00000000..32d9fc85 --- /dev/null +++ b/VAPs/quicklook/SONDEPARAM/SONDEPARAM_tutorial.ipynb @@ -0,0 +1,838 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# SONDEPARAM.C1 Notebook\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/sondeparam) for more information about this vap." + ] + }, + { + "cell_type": "markdown", + "id": "97097763", + "metadata": {}, + "source": [ + "In this notebook, we demonstrate the workflow to explore ARM vap data (using sondeparam as an example.) Value-added products (VAPs) are higher-order data products that have been analyzed and processed to ease scientists’ use of ARM data in atmospheric research and global climate models. \n", + "Here is the main content we will cover." + ] + }, + { + "cell_type": "markdown", + "id": "eddec40f", + "metadata": {}, + "source": [ + "# Table of Content\n", + "## Access the data\n", + "* How to retrieve the data\n", + "* Data path and file name conventions\n", + "* Load data\n", + "## Explore the data\n", + "* NetCDF Data structure\n", + "* Xarray essentials\n", + "* Xarray Variable\n", + "## Plot the data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "import random\n", + "\n", + "import glob\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "2372d5be", + "metadata": {}, + "source": [ + "## Access the data" + ] + }, + { + "cell_type": "markdown", + "id": "5b0d9684", + "metadata": {}, + "source": [ + "### How to retrieve the data\n", + "We assume the path \"/data/archive\" is available where you are running this notebook. You can use `os.path.exists(\"/data/archive\")` to verify if the path exists at your machine.\n", + "\n", + "\n", + "### Data path and file name conventions\n", + "There several common terminologies regarding ARM data, for example, data-stream-name, data-level, site, facility, instrument, etc. (For more details, please see [ARM Data File Standards Version 1.2](https://www.google.com/search?q=arm+datastream+facility+cite+definition&rlz=1C1GCEJ_enUS1029US1029&ei=hb41ZICrBPukqtsPvMODIA&ved=0ahUKEwjAgeiS0aL-AhV7kmoFHbzhAAQQ4dUDCBA&uact=5&oq=arm+datastream+facility+cite+definition&gs_lcp=Cgxnd3Mtd2l6LXNlcnAQAzIFCAAQogQyBQgAEKIEMgUIABCiBDIFCAAQogQyBQgAEKIEOgoIABBHENYEELADOgoIIRCgARDDBBAKSgQIQRgAUJ4DWIoOYO0PaAFwAXgAgAGMAYgBuQmSAQMyLjmYAQCgAQHIAQjAAQE&sclient=gws-wiz-serp).) \n", + "\n", + "For example, this notebook is called `sondeparam.c1`, where `sondeparam` is the \"datastream name\", and the `{process.ds_class_level}}` is the \"data level\".\n", + "\n", + "This datastream also contains site `guc` and facility `M1`. (Note: individual datastream might have multiple site-facility pairs.)\n", + "In such a case, the data of this data-stream is stored at `/data/archive/guc/gucsondeparamM1.c1`, which is in the format of `//.`. We can use the following method to assign the data-stream directory `datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )`\n", + "\n", + "The data files under datastream_dir also follows naming conventions. But once reach the datastream_dir level, the most import file naming convention to differentiate the files is \"yyyyMMdd.hhmmss\", which comes handy to filter out files based on datetime. For example, we can use `glob.glob(f'{datastream_dir}/*.200709*.*')` to filter files in 2007 September.\n", + "\n", + "(Note: refer to the https://adc.arm.gov/discovery/#/ and https://adc.arm.gov/solr8/metadata/select API to explore ARM datastream and assoicated available sites and facilities\n", + "\n", + "Please see the following examples in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d7e9eb85", + "metadata": {}, + "outputs": [], + "source": [ + "# Verify if DATA_DIR path exists\n", + "DATA_DIR = \"/data/archive\"\n", + "os.path.exists(DATA_DIR)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "586993fd", + "metadata": {}, + "outputs": [], + "source": [ + "# Speicify datastream_dir following the path conventions and check its existence\n", + "DATASTREAM_NAME = \"sondeparam\"\n", + "DATA_LEVEL = \"c1\"\n", + "site = \"guc\"\n", + "facility = \"M1\"\n", + "datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )\n", + "print(datastream_dir)\n", + "print(os.path.exists(datastream_dir))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0742f7c1", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: list 5 (random) files under datastream_dir\n", + "files = os.listdir(datastream_dir)\n", + "files[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39b98a36", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: get most recent file\n", + "list_of_files = glob.glob(f\"{datastream_dir}/*\") # * means all if need specific format then *.csv\n", + "latest_file = max(list_of_files, key=os.path.getctime)\n", + "latest_file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "902d514e", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus sort datastream files based on datetime\n", + "files = os.listdir(datastream_dir)\n", + "file_sorted = files.copy()\n", + "file_sorted.sort() \n", + "print(file_sorted[:5])\n", + "\n", + "# to reverse\n", + "file_sorted_reverse = files.copy() \n", + "file_sorted_reverse.sort(reverse=True)\n", + "print(file_sorted_reverse[:5])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec5923b2", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: pattern matching\n", + "# filter the 200709** files under datastream_dir\n", + "files_filter = glob.glob(f'{datastream_dir}/*.200709*.*')\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c4a4aa18", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: use armnotebook_utils.file_filter (TODO) to filter files based on datastream info \n" + ] + }, + { + "cell_type": "markdown", + "id": "cfeb9efc", + "metadata": {}, + "source": [ + "### Load data\n", + "The arm data is stored in [NetCDF](https://en.wikipedia.org/wiki/NetCDF#:~:text=%22NetCDF%20(network%20Common%20Data%20Form,format%20for%20representing%20scientific%20data.) format. We can use xarray's `open_dataset` method to load single file, or `open_mfdataset` to open multiple files. (Note: the latter will still return a single xarray dataset object by combining multiple files.)\n", + "\n", + "Note: open_dataset keeps the file handle open and lazy loads its contents. All parameters are passed directly to open_dataset. It is a preferable method over load_dataset for memory-effiency. (more details, please see [xarray.open_dataset](https://docs.xarray.dev/en/stable/generated/xarray.open_dataset.html))\n", + "\n", + "See the following example in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a440a329", + "metadata": {}, + "outputs": [], + "source": [ + "# open single file\n", + "full_path = latest_file\n", + "print(full_path)\n", + "ds_single = xr.open_dataset(full_path)\n", + "ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0143a3d", + "metadata": {}, + "outputs": [], + "source": [ + "print(ds_single)\n", + "print(type(ds_single))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c0f8939", + "metadata": {}, + "outputs": [], + "source": [ + "# open multiple files\n", + "n_files = 3\n", + "full_paths = [os.path.join(datastream_dir, f_path) for f_path in file_sorted_reverse[:n_files]]\n", + "print(full_paths)\n", + "try: # Note: sometimes multiple files cannot be merged, so we used try except here.\n", + " ds_mutiple = xr.open_mfdataset(full_paths)\n", + " # ds_mutiple\n", + " print(type(ds_mutiple))\n", + " print(ds_mutiple)\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "3fa59943", + "metadata": {}, + "source": [ + "## Explore the data" + ] + }, + { + "cell_type": "markdown", + "id": "571f69b9", + "metadata": {}, + "source": [ + "### NetCDF Data structure \n", + "\n", + "\n", + "(If you are confident with NetCDF basics and xarray essentials, feel free to skip this session.)\n", + "Before we dive into data exploration, there are some eseential concpets we should be familiar with (shown below): \n", + "* Dataset\n", + "* Data array\n", + "* Variable\n", + "* Dimenssion\n", + "* Coordinate\n", + "* Data Type\n", + "* Meta Data (Attributes)\n", + "\n", + "We will not go into details about NetCDF basics and here are some references you might find helpful\n", + "* [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#)\n", + "* [Network Common Data Form (NetCDF)](https://www.unidata.ucar.edu/software/netcdf/) and [A Brief History of (netCDF) Time](https://www.unidata.ucar.edu/software/netcdf/time/recs.html)\n", + "\n", + "\n", + "\n", + "### Xarray essentials\n", + "Earlier we introduced the xarray pacakge and used `open_dataset` and `open_mfdataset` to retrieve NetCDF data file as an xarray Dataset object. Recall that you can review the data in a notebook by using `print(ds)` or `ds`. \n", + "\n", + "Using xarray to retrieve the aforementioned NetCDF basics is straightforwared. In general,\n", + "* Dataset: `ds`\n", + "* Data array: `ds.variables`\n", + "* Variable: `ds.ds.variables`\n", + "* Dimenssion: `ds.dims`\n", + "* Coordinate: `ds.coords`\n", + "* Data Type: `type`\n", + "* Meta Data (Attributes)\n", + "\n", + "Also, here are some references if you are new to xarray\n", + "* [Xarray in 45 minutes](https://tutorial.xarray.dev/overview/xarray-in-45-min.html)\n", + "* [Handling NetCDF Files using XArray for Absolute Beginners](https://towardsdatascience.com/handling-netcdf-files-using-xarray-for-absolute-beginners-111a8ab4463f)\n", + "\n", + "Try the following commands in action\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b3eac323", + "metadata": {}, + "outputs": [], + "source": [ + "ds = ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77ecf85d", + "metadata": {}, + "outputs": [], + "source": [ + "# Dataset \n", + "ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25e7de09", + "metadata": {}, + "outputs": [], + "source": [ + "# Data array, Variable\n", + "# Note: the info can be overwhelming. Do not attempt to grasp everything at the first glance.\n", + "ds.variables" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c41b67e", + "metadata": {}, + "outputs": [], + "source": [ + "# dimenssions\n", + "ds.dims" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "156f1dfc", + "metadata": {}, + "outputs": [], + "source": [ + "# coordinates\n", + "ds.coords" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "277d6064", + "metadata": {}, + "outputs": [], + "source": [ + "# Meta Data (Attributes)\n", + "ds.attrs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d334681f", + "metadata": {}, + "outputs": [], + "source": [ + "# type\n", + "print(type(ds))\n", + "print(type(ds.variables))\n", + "print(type(ds.dims))\n", + "print(type(ds.coords))\n", + "print(type(ds.attrs))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "643399d6", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: retrieve variable names only\n", + "list(ds.variables)" + ] + }, + { + "cell_type": "markdown", + "id": "1cf36878", + "metadata": {}, + "source": [ + "#### Discussion: variable vs. coordinates vs. dimenssions. \n", + "From [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#):\n", + "\n", + "* Dimession: A dimension may be used to represent a real physical dimension, for example, time, latitude, longitude, or height. A dimension might also be used to index other quantities, for example station or model-run-number.\n", + "A netCDF dimension has both a name and a length. A dimension length is an arbitrary positive integer, except that one dimension in a netCDF dataset can have the length UNLIMITED.\n", + "\n", + "* Variables: Variables are used to store the bulk of the data in a netCDF dataset. A variable represents an array of values of the same type. A scalar value is treated as a 0-dimensional array. A variable has a name, a data type, and a shape described by its list of dimensions specified when the variable is created. A variable may also have associated attributes, which may be added, deleted or changed after the variable is created.\n", + "\n", + "* Coordinate (Variables): It is legal for a variable to have the same name as a dimension. Such variables have no special meaning to the netCDF library. However there is a convention that such variables should be treated in a special way by software using this library.\n", + "A variable with the same name as a dimension is called a coordinate variable. It typically defines a physical coordinate corresponding to that dimension.\n", + "\n", + "\n", + "Tips: By definition, coordinates and dimenssions are also variable. For example ds.time is a coordinate but it is also a special variable. But in practice, when we talk about variable, it implies regular/non-coordinate variable. We will use this convention for the remaining notebook. " + ] + }, + { + "cell_type": "markdown", + "id": "d47120f9", + "metadata": {}, + "source": [ + "### Xarray Variable\n", + "\n", + "\n", + "We can use `ds.variables` to access varialbes of a dataset. But many times viewing print-out the whole datasets can be overwhemling. Instead, we would work on the individual variable (also called Xarray data array.) using the `ds[var_name]` syntax, where `var_name` is the name of the variable. See the following examples in action.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5a57e136", + "metadata": {}, + "outputs": [], + "source": [ + "print(type(ds[\"time\"]))\n", + "ds[\"time\"]" + ] + }, + { + "cell_type": "markdown", + "id": "51ec643e", + "metadata": {}, + "source": [ + "#### Variable properties\n", + "For individual varible, the following properties are mostly used with the assocaited xarray method to retrieve them. (assuming var = ds[var_name])\n", + "* name: `var.name`\n", + "* data content: `var.data`\n", + "* attributes: `var.attrs`\n", + "* dimenstions: `var.dims`\n", + "* data type: `var.data.dtype`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a632a525", + "metadata": {}, + "outputs": [], + "source": [ + "var_name = \"time\"\n", + "var = ds[var_name]\n", + "\n", + "print(\"var.name: \\n\", var.name, \"\\n\")\n", + "print(\"var.data: \\n\", var.data, \"\\n\")\n", + "print(\"var.attrs: \\n\", var.attrs, \"\\n\")\n", + "print(\"var.dims: \\n\", var.dims, \"\\n\")\n", + "print(\"var.data.dtype: \\n\", var.data.dtype, \"\\n\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b256b07f", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: indivdual variable info table (collect and display variable info as a tabular format)\n", + "df_info = pd.DataFrame()\n", + "df_info[\"var_name\"] = list(ds.variables)\n", + "df_info[\"dims\"] = df_info.var_name.apply(lambda x: ds[x].dims)\n", + "df_info[\"is_dim\"] = df_info.var_name.apply(lambda x: x in ds.dims)\n", + "df_info[\"n_dim\"] = df_info.var_name.apply(lambda x: len(ds[x].dims))\n", + "df_info[\"attrs\"] = df_info.var_name.apply(lambda x: ds[x].attrs)\n", + "df_info[\"dtype\"] = df_info.var_name.apply(lambda x: ds[x].data.dtype)\n", + "df_info" + ] + }, + { + "cell_type": "markdown", + "id": "e6a36b96", + "metadata": {}, + "source": [ + "### Data cleaning/Preprocessing (skipped)\n", + "Data cleaning and preprocessing is an important stage in the the data analysis pipeline. However it will be out of the scope of this notebook. For more information about data cleaning and preprocessing basics, here are some references. \n", + "* [Xarray Fundamentals](https://earth-env-data-science.github.io/lectures/xarray/xarray.html)\n", + "* [Xarray Tutorial — Pangeo Gallery documentation](http://gallery.pangeo.io/repos/pangeo-data/pangeo-tutorial-gallery/xarray.html)\n", + "* [Pythonic Data Cleaning With pandas and NumPy](https://realpython.com/python-data-cleaning-numpy-pandas/)\n", + "* [Pandas - Cleaning Data](https://www.w3schools.com/python/pandas/pandas_cleaning.asp)" + ] + }, + { + "cell_type": "markdown", + "id": "ae6e1a69", + "metadata": {}, + "source": [ + "### Plotting\n", + "Data visualization (or plotting) is another important data analysis topic and deserves its own discussion. Here in this notebook we will only demonstrate basic tool for simple data visualization tasks.\n", + "\n", + "Here are some reference you might find useful:\n", + "* xarray plotting: https://docs.xarray.dev/en/stable/user-guide/plotting.html\n", + "* Atmospheric Community Toolkit (ACT): https://arm-doe.github.io/ACT/index.html\n", + "\n", + "Note: this notebook is auto-generated using a template. It uses a general idea to select variable(s) to plot and is not customized to each indivdual notebook. Feel free to change the variables in intrrests, especially certain figure is failed to plot." + ] + }, + { + "cell_type": "markdown", + "id": "7e417fb2", + "metadata": {}, + "source": [ + "#### 1-dimenssional basic time series plot\n", + "\n", + "For the following plot we would like to find variables such that\n", + "* it has one and only one dimession\n", + "* \"time\" is its coordinate variable\n", + "* it is not a dimenssion itself,\n", + "* it is not a special variable with substrings within [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "\n", + "Please see the following example in action. (For more details about pandas filtering, please see the following references.)\n", + "* [pandas: multiple conditions while indexing data frame](https://stackoverflow.com/questions/22591174/pandas-multiple-conditions-while-indexing-data-frame-unexpected-behavior)\n", + "* [pandas.DataFrame.apply](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.apply.html)\n", + "* [How to test if a string contains one of the substrings in a list...](https://stackoverflow.com/questions/26577516/how-to-test-if-a-string-contains-one-of-the-substrings-in-a-list-in-pandas" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30edac2d", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==1, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter = df_info[(df_info.n_dim == 1) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4777a659", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_1d = df_filter.var_name.values[0]\n", + " var_1d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb5ae985", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " ds[var_1d].plot()\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "9e41066e", + "metadata": {}, + "source": [ + "#### 2-dimenssional basic plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09bd4adc", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==2, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_2 = df_info[(df_info.n_dim == 2) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2cdea1a9", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_2d = df_filter_2.var_name.values[0]\n", + " var_2d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5f48879", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " print(ds[var_2d].dims)\n", + " # ds[var_2d].plot()\n", + "\n", + " # conventionally, use \"time\" as x-axis\n", + " ds[var_2d].plot(x=\"time\")\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "7554b2a6", + "metadata": {}, + "source": [ + "#### qc-plotting (optional)\n", + "\n", + "Note: act qc plotting has more strict requirement, one of them is the associated qc variable need to have \"flag_masks\" attributes. Which is added by using ds = act.io.armfiles.read_netcdf(files_list), then ds.clean.cleanup()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71c2096f", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " ds_act = act.io.armfiles.read_netcdf(full_path)\n", + " print(type(ds_act))\n", + " ds_act.clean.cleanup()\n", + "\n", + " # or \n", + " # ds.clean.cleanup()\n", + "except Exception as e:\n", + " print(\"ERROR\", e)\n", + " ds_act = ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "844f8505", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter valid variables for ACT qc plotting\n", + "condition = (\"qc_\" + df_info.var_name).apply(lambda x: ds_act[x].attrs.get(\"flag_masks\") is not None \n", + " if x in list(ds.variables) else False)\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_3 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings))) &\n", + " condition\n", + " ]\n", + "df_filter_3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd38cc6", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "try:\n", + " qc_variable = df_filter_3.var_name.values[0]\n", + " print(qc_variable)\n", + "except Exception as e:\n", + " print(e)\n", + " \n", + "try:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + " qc_ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "d76e4d27", + "metadata": {}, + "source": [ + "#### bonus: choose variables to plot from a dropdown menu " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb733804", + "metadata": {}, + "outputs": [], + "source": [ + "# Valid variables filtering\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_4 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82d5c20d", + "metadata": {}, + "outputs": [], + "source": [ + "# example 1: using xarray plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2fc9aaa4", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# fig, ax = plt.subplots(figsize=(10, 4))\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "# fig.clear() # Remove old lines from plot and plot new one\n", + "# if len(ds[var].dims)==2:\n", + "# ds[var].plot(x=\"time\", add_colorbar=False)\n", + "# else:\n", + "# ds[var].plot()\n", + "# plt.grid()\n", + "# plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fabdd802", + "metadata": {}, + "outputs": [], + "source": [ + "# example 2: using act plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "962b4186", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "\n", + "\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "\n", + "# i_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + "# i_display.add_subplots((1,), figsize=(10, 4))\n", + "# ax = i_display.plot(var, subplot_index=(0,), set_title=f\"{var} ({ds_act[var].attrs['long_name']})\",)\n", + "\n", + "# ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "# ax.grid()\n", + "# plt.show()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/SONDEPARAM/sondeparam.c1.ipynb b/VAPs/quicklook/SONDEPARAM/sondeparam.c1.ipynb new file mode 100644 index 00000000..e9d8e3c7 --- /dev/null +++ b/VAPs/quicklook/SONDEPARAM/sondeparam.c1.ipynb @@ -0,0 +1,297 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# SONDEPARAM.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/sondeparam) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'sondeparam'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2023-06-15', 'facility': 'M1', 'site': 'guc', 'start_date': '2021-09-01'}, {'end_date': '2023-12-18', 'facility': 'C1', 'site': 'ena', 'start_date': '2013-09-28'}, {'end_date': '2022-10-01', 'facility': 'M1', 'site': 'hou', 'start_date': '2021-08-28'}, {'end_date': '2022-09-25', 'facility': 'S1', 'site': 'hou', 'start_date': '2021-08-28'}, {'end_date': '2015-12-01', 'facility': 'M1', 'site': 'mao', 'start_date': '2013-12-17'}, {'end_date': '2023-12-18', 'facility': 'C1', 'site': 'sgp', 'start_date': '2001-04-01'}, {'end_date': '2014-09-12', 'facility': 'M1', 'site': 'tmp', 'start_date': '2014-01-21'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2023-12-16'\n", + "date_end = '2023-12-18'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['cape', 'cin', 'lcl']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'cape'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/SP2-AIR/SP2-AIR_tutorial.ipynb b/VAPs/quicklook/SP2-AIR/SP2-AIR_tutorial.ipynb new file mode 100644 index 00000000..c18b98d1 --- /dev/null +++ b/VAPs/quicklook/SP2-AIR/SP2-AIR_tutorial.ipynb @@ -0,0 +1,838 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# AAFSP2RBC10S.C1 Notebook\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/sp2-air) for more information about this vap." + ] + }, + { + "cell_type": "markdown", + "id": "97097763", + "metadata": {}, + "source": [ + "In this notebook, we demonstrate the workflow to explore ARM vap data (using aafsp2rbc10s as an example.) Value-added products (VAPs) are higher-order data products that have been analyzed and processed to ease scientists’ use of ARM data in atmospheric research and global climate models. \n", + "Here is the main content we will cover." + ] + }, + { + "cell_type": "markdown", + "id": "eddec40f", + "metadata": {}, + "source": [ + "# Table of Content\n", + "## Access the data\n", + "* How to retrieve the data\n", + "* Data path and file name conventions\n", + "* Load data\n", + "## Explore the data\n", + "* NetCDF Data structure\n", + "* Xarray essentials\n", + "* Xarray Variable\n", + "## Plot the data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "import random\n", + "\n", + "import glob\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "2372d5be", + "metadata": {}, + "source": [ + "## Access the data" + ] + }, + { + "cell_type": "markdown", + "id": "5b0d9684", + "metadata": {}, + "source": [ + "### How to retrieve the data\n", + "We assume the path \"/data/archive\" is available where you are running this notebook. You can use `os.path.exists(\"/data/archive\")` to verify if the path exists at your machine.\n", + "\n", + "\n", + "### Data path and file name conventions\n", + "There several common terminologies regarding ARM data, for example, data-stream-name, data-level, site, facility, instrument, etc. (For more details, please see [ARM Data File Standards Version 1.2](https://www.google.com/search?q=arm+datastream+facility+cite+definition&rlz=1C1GCEJ_enUS1029US1029&ei=hb41ZICrBPukqtsPvMODIA&ved=0ahUKEwjAgeiS0aL-AhV7kmoFHbzhAAQQ4dUDCBA&uact=5&oq=arm+datastream+facility+cite+definition&gs_lcp=Cgxnd3Mtd2l6LXNlcnAQAzIFCAAQogQyBQgAEKIEMgUIABCiBDIFCAAQogQyBQgAEKIEOgoIABBHENYEELADOgoIIRCgARDDBBAKSgQIQRgAUJ4DWIoOYO0PaAFwAXgAgAGMAYgBuQmSAQMyLjmYAQCgAQHIAQjAAQE&sclient=gws-wiz-serp).) \n", + "\n", + "For example, this notebook is called `aafsp2rbc10s.c1`, where `aafsp2rbc10s` is the \"datastream name\", and the `{process.ds_class_level}}` is the \"data level\".\n", + "\n", + "This datastream also contains site `cor` and facility `F1`. (Note: individual datastream might have multiple site-facility pairs.)\n", + "In such a case, the data of this data-stream is stored at `/data/archive/cor/coraafsp2rbc10sF1.c1`, which is in the format of `//.`. We can use the following method to assign the data-stream directory `datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )`\n", + "\n", + "The data files under datastream_dir also follows naming conventions. But once reach the datastream_dir level, the most import file naming convention to differentiate the files is \"yyyyMMdd.hhmmss\", which comes handy to filter out files based on datetime. For example, we can use `glob.glob(f'{datastream_dir}/*.200709*.*')` to filter files in 2007 September.\n", + "\n", + "(Note: refer to the https://adc.arm.gov/discovery/#/ and https://adc.arm.gov/solr8/metadata/select API to explore ARM datastream and assoicated available sites and facilities\n", + "\n", + "Please see the following examples in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d7e9eb85", + "metadata": {}, + "outputs": [], + "source": [ + "# Verify if DATA_DIR path exists\n", + "DATA_DIR = \"/data/archive\"\n", + "os.path.exists(DATA_DIR)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "586993fd", + "metadata": {}, + "outputs": [], + "source": [ + "# Speicify datastream_dir following the path conventions and check its existence\n", + "DATASTREAM_NAME = \"aafsp2rbc10s\"\n", + "DATA_LEVEL = \"c1\"\n", + "site = \"cor\"\n", + "facility = \"F1\"\n", + "datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )\n", + "print(datastream_dir)\n", + "print(os.path.exists(datastream_dir))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0742f7c1", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: list 5 (random) files under datastream_dir\n", + "files = os.listdir(datastream_dir)\n", + "files[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39b98a36", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: get most recent file\n", + "list_of_files = glob.glob(f\"{datastream_dir}/*\") # * means all if need specific format then *.csv\n", + "latest_file = max(list_of_files, key=os.path.getctime)\n", + "latest_file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "902d514e", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus sort datastream files based on datetime\n", + "files = os.listdir(datastream_dir)\n", + "file_sorted = files.copy()\n", + "file_sorted.sort() \n", + "print(file_sorted[:5])\n", + "\n", + "# to reverse\n", + "file_sorted_reverse = files.copy() \n", + "file_sorted_reverse.sort(reverse=True)\n", + "print(file_sorted_reverse[:5])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec5923b2", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: pattern matching\n", + "# filter the 200709** files under datastream_dir\n", + "files_filter = glob.glob(f'{datastream_dir}/*.200709*.*')\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c4a4aa18", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: use armnotebook_utils.file_filter (TODO) to filter files based on datastream info \n" + ] + }, + { + "cell_type": "markdown", + "id": "cfeb9efc", + "metadata": {}, + "source": [ + "### Load data\n", + "The arm data is stored in [NetCDF](https://en.wikipedia.org/wiki/NetCDF#:~:text=%22NetCDF%20(network%20Common%20Data%20Form,format%20for%20representing%20scientific%20data.) format. We can use xarray's `open_dataset` method to load single file, or `open_mfdataset` to open multiple files. (Note: the latter will still return a single xarray dataset object by combining multiple files.)\n", + "\n", + "Note: open_dataset keeps the file handle open and lazy loads its contents. All parameters are passed directly to open_dataset. It is a preferable method over load_dataset for memory-effiency. (more details, please see [xarray.open_dataset](https://docs.xarray.dev/en/stable/generated/xarray.open_dataset.html))\n", + "\n", + "See the following example in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a440a329", + "metadata": {}, + "outputs": [], + "source": [ + "# open single file\n", + "full_path = latest_file\n", + "print(full_path)\n", + "ds_single = xr.open_dataset(full_path)\n", + "ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0143a3d", + "metadata": {}, + "outputs": [], + "source": [ + "print(ds_single)\n", + "print(type(ds_single))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c0f8939", + "metadata": {}, + "outputs": [], + "source": [ + "# open multiple files\n", + "n_files = 3\n", + "full_paths = [os.path.join(datastream_dir, f_path) for f_path in file_sorted_reverse[:n_files]]\n", + "print(full_paths)\n", + "try: # Note: sometimes multiple files cannot be merged, so we used try except here.\n", + " ds_mutiple = xr.open_mfdataset(full_paths)\n", + " # ds_mutiple\n", + " print(type(ds_mutiple))\n", + " print(ds_mutiple)\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "3fa59943", + "metadata": {}, + "source": [ + "## Explore the data" + ] + }, + { + "cell_type": "markdown", + "id": "571f69b9", + "metadata": {}, + "source": [ + "### NetCDF Data structure \n", + "\n", + "\n", + "(If you are confident with NetCDF basics and xarray essentials, feel free to skip this session.)\n", + "Before we dive into data exploration, there are some eseential concpets we should be familiar with (shown below): \n", + "* Dataset\n", + "* Data array\n", + "* Variable\n", + "* Dimenssion\n", + "* Coordinate\n", + "* Data Type\n", + "* Meta Data (Attributes)\n", + "\n", + "We will not go into details about NetCDF basics and here are some references you might find helpful\n", + "* [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#)\n", + "* [Network Common Data Form (NetCDF)](https://www.unidata.ucar.edu/software/netcdf/) and [A Brief History of (netCDF) Time](https://www.unidata.ucar.edu/software/netcdf/time/recs.html)\n", + "\n", + "\n", + "\n", + "### Xarray essentials\n", + "Earlier we introduced the xarray pacakge and used `open_dataset` and `open_mfdataset` to retrieve NetCDF data file as an xarray Dataset object. Recall that you can review the data in a notebook by using `print(ds)` or `ds`. \n", + "\n", + "Using xarray to retrieve the aforementioned NetCDF basics is straightforwared. In general,\n", + "* Dataset: `ds`\n", + "* Data array: `ds.variables`\n", + "* Variable: `ds.ds.variables`\n", + "* Dimenssion: `ds.dims`\n", + "* Coordinate: `ds.coords`\n", + "* Data Type: `type`\n", + "* Meta Data (Attributes)\n", + "\n", + "Also, here are some references if you are new to xarray\n", + "* [Xarray in 45 minutes](https://tutorial.xarray.dev/overview/xarray-in-45-min.html)\n", + "* [Handling NetCDF Files using XArray for Absolute Beginners](https://towardsdatascience.com/handling-netcdf-files-using-xarray-for-absolute-beginners-111a8ab4463f)\n", + "\n", + "Try the following commands in action\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b3eac323", + "metadata": {}, + "outputs": [], + "source": [ + "ds = ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77ecf85d", + "metadata": {}, + "outputs": [], + "source": [ + "# Dataset \n", + "ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25e7de09", + "metadata": {}, + "outputs": [], + "source": [ + "# Data array, Variable\n", + "# Note: the info can be overwhelming. Do not attempt to grasp everything at the first glance.\n", + "ds.variables" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c41b67e", + "metadata": {}, + "outputs": [], + "source": [ + "# dimenssions\n", + "ds.dims" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "156f1dfc", + "metadata": {}, + "outputs": [], + "source": [ + "# coordinates\n", + "ds.coords" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "277d6064", + "metadata": {}, + "outputs": [], + "source": [ + "# Meta Data (Attributes)\n", + "ds.attrs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d334681f", + "metadata": {}, + "outputs": [], + "source": [ + "# type\n", + "print(type(ds))\n", + "print(type(ds.variables))\n", + "print(type(ds.dims))\n", + "print(type(ds.coords))\n", + "print(type(ds.attrs))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "643399d6", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: retrieve variable names only\n", + "list(ds.variables)" + ] + }, + { + "cell_type": "markdown", + "id": "1cf36878", + "metadata": {}, + "source": [ + "#### Discussion: variable vs. coordinates vs. dimenssions. \n", + "From [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#):\n", + "\n", + "* Dimession: A dimension may be used to represent a real physical dimension, for example, time, latitude, longitude, or height. A dimension might also be used to index other quantities, for example station or model-run-number.\n", + "A netCDF dimension has both a name and a length. A dimension length is an arbitrary positive integer, except that one dimension in a netCDF dataset can have the length UNLIMITED.\n", + "\n", + "* Variables: Variables are used to store the bulk of the data in a netCDF dataset. A variable represents an array of values of the same type. A scalar value is treated as a 0-dimensional array. A variable has a name, a data type, and a shape described by its list of dimensions specified when the variable is created. A variable may also have associated attributes, which may be added, deleted or changed after the variable is created.\n", + "\n", + "* Coordinate (Variables): It is legal for a variable to have the same name as a dimension. Such variables have no special meaning to the netCDF library. However there is a convention that such variables should be treated in a special way by software using this library.\n", + "A variable with the same name as a dimension is called a coordinate variable. It typically defines a physical coordinate corresponding to that dimension.\n", + "\n", + "\n", + "Tips: By definition, coordinates and dimenssions are also variable. For example ds.time is a coordinate but it is also a special variable. But in practice, when we talk about variable, it implies regular/non-coordinate variable. We will use this convention for the remaining notebook. " + ] + }, + { + "cell_type": "markdown", + "id": "d47120f9", + "metadata": {}, + "source": [ + "### Xarray Variable\n", + "\n", + "\n", + "We can use `ds.variables` to access varialbes of a dataset. But many times viewing print-out the whole datasets can be overwhemling. Instead, we would work on the individual variable (also called Xarray data array.) using the `ds[var_name]` syntax, where `var_name` is the name of the variable. See the following examples in action.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5a57e136", + "metadata": {}, + "outputs": [], + "source": [ + "print(type(ds[\"time\"]))\n", + "ds[\"time\"]" + ] + }, + { + "cell_type": "markdown", + "id": "51ec643e", + "metadata": {}, + "source": [ + "#### Variable properties\n", + "For individual varible, the following properties are mostly used with the assocaited xarray method to retrieve them. (assuming var = ds[var_name])\n", + "* name: `var.name`\n", + "* data content: `var.data`\n", + "* attributes: `var.attrs`\n", + "* dimenstions: `var.dims`\n", + "* data type: `var.data.dtype`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a632a525", + "metadata": {}, + "outputs": [], + "source": [ + "var_name = \"time\"\n", + "var = ds[var_name]\n", + "\n", + "print(\"var.name: \\n\", var.name, \"\\n\")\n", + "print(\"var.data: \\n\", var.data, \"\\n\")\n", + "print(\"var.attrs: \\n\", var.attrs, \"\\n\")\n", + "print(\"var.dims: \\n\", var.dims, \"\\n\")\n", + "print(\"var.data.dtype: \\n\", var.data.dtype, \"\\n\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b256b07f", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: indivdual variable info table (collect and display variable info as a tabular format)\n", + "df_info = pd.DataFrame()\n", + "df_info[\"var_name\"] = list(ds.variables)\n", + "df_info[\"dims\"] = df_info.var_name.apply(lambda x: ds[x].dims)\n", + "df_info[\"is_dim\"] = df_info.var_name.apply(lambda x: x in ds.dims)\n", + "df_info[\"n_dim\"] = df_info.var_name.apply(lambda x: len(ds[x].dims))\n", + "df_info[\"attrs\"] = df_info.var_name.apply(lambda x: ds[x].attrs)\n", + "df_info[\"dtype\"] = df_info.var_name.apply(lambda x: ds[x].data.dtype)\n", + "df_info" + ] + }, + { + "cell_type": "markdown", + "id": "e6a36b96", + "metadata": {}, + "source": [ + "### Data cleaning/Preprocessing (skipped)\n", + "Data cleaning and preprocessing is an important stage in the the data analysis pipeline. However it will be out of the scope of this notebook. For more information about data cleaning and preprocessing basics, here are some references. \n", + "* [Xarray Fundamentals](https://earth-env-data-science.github.io/lectures/xarray/xarray.html)\n", + "* [Xarray Tutorial — Pangeo Gallery documentation](http://gallery.pangeo.io/repos/pangeo-data/pangeo-tutorial-gallery/xarray.html)\n", + "* [Pythonic Data Cleaning With pandas and NumPy](https://realpython.com/python-data-cleaning-numpy-pandas/)\n", + "* [Pandas - Cleaning Data](https://www.w3schools.com/python/pandas/pandas_cleaning.asp)" + ] + }, + { + "cell_type": "markdown", + "id": "ae6e1a69", + "metadata": {}, + "source": [ + "### Plotting\n", + "Data visualization (or plotting) is another important data analysis topic and deserves its own discussion. Here in this notebook we will only demonstrate basic tool for simple data visualization tasks.\n", + "\n", + "Here are some reference you might find useful:\n", + "* xarray plotting: https://docs.xarray.dev/en/stable/user-guide/plotting.html\n", + "* Atmospheric Community Toolkit (ACT): https://arm-doe.github.io/ACT/index.html\n", + "\n", + "Note: this notebook is auto-generated using a template. It uses a general idea to select variable(s) to plot and is not customized to each indivdual notebook. Feel free to change the variables in intrrests, especially certain figure is failed to plot." + ] + }, + { + "cell_type": "markdown", + "id": "7e417fb2", + "metadata": {}, + "source": [ + "#### 1-dimenssional basic time series plot\n", + "\n", + "For the following plot we would like to find variables such that\n", + "* it has one and only one dimession\n", + "* \"time\" is its coordinate variable\n", + "* it is not a dimenssion itself,\n", + "* it is not a special variable with substrings within [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "\n", + "Please see the following example in action. (For more details about pandas filtering, please see the following references.)\n", + "* [pandas: multiple conditions while indexing data frame](https://stackoverflow.com/questions/22591174/pandas-multiple-conditions-while-indexing-data-frame-unexpected-behavior)\n", + "* [pandas.DataFrame.apply](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.apply.html)\n", + "* [How to test if a string contains one of the substrings in a list...](https://stackoverflow.com/questions/26577516/how-to-test-if-a-string-contains-one-of-the-substrings-in-a-list-in-pandas" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30edac2d", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==1, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter = df_info[(df_info.n_dim == 1) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4777a659", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_1d = df_filter.var_name.values[0]\n", + " var_1d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb5ae985", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " ds[var_1d].plot()\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "9e41066e", + "metadata": {}, + "source": [ + "#### 2-dimenssional basic plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09bd4adc", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==2, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_2 = df_info[(df_info.n_dim == 2) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2cdea1a9", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_2d = df_filter_2.var_name.values[0]\n", + " var_2d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5f48879", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " print(ds[var_2d].dims)\n", + " # ds[var_2d].plot()\n", + "\n", + " # conventionally, use \"time\" as x-axis\n", + " ds[var_2d].plot(x=\"time\")\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "7554b2a6", + "metadata": {}, + "source": [ + "#### qc-plotting (optional)\n", + "\n", + "Note: act qc plotting has more strict requirement, one of them is the associated qc variable need to have \"flag_masks\" attributes. Which is added by using ds = act.io.armfiles.read_netcdf(files_list), then ds.clean.cleanup()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71c2096f", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " ds_act = act.io.armfiles.read_netcdf(full_path)\n", + " print(type(ds_act))\n", + " ds_act.clean.cleanup()\n", + "\n", + " # or \n", + " # ds.clean.cleanup()\n", + "except Exception as e:\n", + " print(\"ERROR\", e)\n", + " ds_act = ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "844f8505", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter valid variables for ACT qc plotting\n", + "condition = (\"qc_\" + df_info.var_name).apply(lambda x: ds_act[x].attrs.get(\"flag_masks\") is not None \n", + " if x in list(ds.variables) else False)\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_3 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings))) &\n", + " condition\n", + " ]\n", + "df_filter_3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd38cc6", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "try:\n", + " qc_variable = df_filter_3.var_name.values[0]\n", + " print(qc_variable)\n", + "except Exception as e:\n", + " print(e)\n", + " \n", + "try:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + " qc_ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "d76e4d27", + "metadata": {}, + "source": [ + "#### bonus: choose variables to plot from a dropdown menu " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb733804", + "metadata": {}, + "outputs": [], + "source": [ + "# Valid variables filtering\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_4 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82d5c20d", + "metadata": {}, + "outputs": [], + "source": [ + "# example 1: using xarray plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2fc9aaa4", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# fig, ax = plt.subplots(figsize=(10, 4))\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "# fig.clear() # Remove old lines from plot and plot new one\n", + "# if len(ds[var].dims)==2:\n", + "# ds[var].plot(x=\"time\", add_colorbar=False)\n", + "# else:\n", + "# ds[var].plot()\n", + "# plt.grid()\n", + "# plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fabdd802", + "metadata": {}, + "outputs": [], + "source": [ + "# example 2: using act plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "962b4186", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "\n", + "\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "\n", + "# i_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + "# i_display.add_subplots((1,), figsize=(10, 4))\n", + "# ax = i_display.plot(var, subplot_index=(0,), set_title=f\"{var} ({ds_act[var].attrs['long_name']})\",)\n", + "\n", + "# ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "# ax.grid()\n", + "# plt.show()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/SP2-AIR/aafsp2rbc10s.c1.ipynb b/VAPs/quicklook/SP2-AIR/aafsp2rbc10s.c1.ipynb new file mode 100644 index 00000000..27259847 --- /dev/null +++ b/VAPs/quicklook/SP2-AIR/aafsp2rbc10s.c1.ipynb @@ -0,0 +1,339 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# AAFSP2RBC10S.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/sp2-air) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'aafsp2rbc10s'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2018-12-08', 'facility': 'F1', 'site': 'cor', 'start_date': '2018-11-04'}, {'end_date': '2018-02-18', 'facility': 'F1', 'site': 'ena', 'start_date': '2017-06-23'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'cor', 'F1' )\n", + "\n", + "date_start = '2018-12-06'\n", + "date_end = '2018-12-08'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['rBC', 'N_dN_rBC']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'rBC'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'rBC'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/SPHOTCOD/SPHOTCOD_tutorial.ipynb b/VAPs/quicklook/SPHOTCOD/SPHOTCOD_tutorial.ipynb new file mode 100644 index 00000000..4be5635f --- /dev/null +++ b/VAPs/quicklook/SPHOTCOD/SPHOTCOD_tutorial.ipynb @@ -0,0 +1,838 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# SPHOTCOD2CHIU.C1 Notebook\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/sphotcod) for more information about this vap." + ] + }, + { + "cell_type": "markdown", + "id": "97097763", + "metadata": {}, + "source": [ + "In this notebook, we demonstrate the workflow to explore ARM vap data (using sphotcod2chiu as an example.) Value-added products (VAPs) are higher-order data products that have been analyzed and processed to ease scientists’ use of ARM data in atmospheric research and global climate models. \n", + "Here is the main content we will cover." + ] + }, + { + "cell_type": "markdown", + "id": "eddec40f", + "metadata": {}, + "source": [ + "# Table of Content\n", + "## Access the data\n", + "* How to retrieve the data\n", + "* Data path and file name conventions\n", + "* Load data\n", + "## Explore the data\n", + "* NetCDF Data structure\n", + "* Xarray essentials\n", + "* Xarray Variable\n", + "## Plot the data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "import random\n", + "\n", + "import glob\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "2372d5be", + "metadata": {}, + "source": [ + "## Access the data" + ] + }, + { + "cell_type": "markdown", + "id": "5b0d9684", + "metadata": {}, + "source": [ + "### How to retrieve the data\n", + "We assume the path \"/data/archive\" is available where you are running this notebook. You can use `os.path.exists(\"/data/archive\")` to verify if the path exists at your machine.\n", + "\n", + "\n", + "### Data path and file name conventions\n", + "There several common terminologies regarding ARM data, for example, data-stream-name, data-level, site, facility, instrument, etc. (For more details, please see [ARM Data File Standards Version 1.2](https://www.google.com/search?q=arm+datastream+facility+cite+definition&rlz=1C1GCEJ_enUS1029US1029&ei=hb41ZICrBPukqtsPvMODIA&ved=0ahUKEwjAgeiS0aL-AhV7kmoFHbzhAAQQ4dUDCBA&uact=5&oq=arm+datastream+facility+cite+definition&gs_lcp=Cgxnd3Mtd2l6LXNlcnAQAzIFCAAQogQyBQgAEKIEMgUIABCiBDIFCAAQogQyBQgAEKIEOgoIABBHENYEELADOgoIIRCgARDDBBAKSgQIQRgAUJ4DWIoOYO0PaAFwAXgAgAGMAYgBuQmSAQMyLjmYAQCgAQHIAQjAAQE&sclient=gws-wiz-serp).) \n", + "\n", + "For example, this notebook is called `sphotcod2chiu.c1`, where `sphotcod2chiu` is the \"datastream name\", and the `{process.ds_class_level}}` is the \"data level\".\n", + "\n", + "This datastream also contains site `ena` and facility `C1`. (Note: individual datastream might have multiple site-facility pairs.)\n", + "In such a case, the data of this data-stream is stored at `/data/archive/ena/enasphotcod2chiuC1.c1`, which is in the format of `//.`. We can use the following method to assign the data-stream directory `datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )`\n", + "\n", + "The data files under datastream_dir also follows naming conventions. But once reach the datastream_dir level, the most import file naming convention to differentiate the files is \"yyyyMMdd.hhmmss\", which comes handy to filter out files based on datetime. For example, we can use `glob.glob(f'{datastream_dir}/*.200709*.*')` to filter files in 2007 September.\n", + "\n", + "(Note: refer to the https://adc.arm.gov/discovery/#/ and https://adc.arm.gov/solr8/metadata/select API to explore ARM datastream and assoicated available sites and facilities\n", + "\n", + "Please see the following examples in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d7e9eb85", + "metadata": {}, + "outputs": [], + "source": [ + "# Verify if DATA_DIR path exists\n", + "DATA_DIR = \"/data/archive\"\n", + "os.path.exists(DATA_DIR)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "586993fd", + "metadata": {}, + "outputs": [], + "source": [ + "# Speicify datastream_dir following the path conventions and check its existence\n", + "DATASTREAM_NAME = \"sphotcod2chiu\"\n", + "DATA_LEVEL = \"c1\"\n", + "site = \"ena\"\n", + "facility = \"C1\"\n", + "datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )\n", + "print(datastream_dir)\n", + "print(os.path.exists(datastream_dir))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0742f7c1", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: list 5 (random) files under datastream_dir\n", + "files = os.listdir(datastream_dir)\n", + "files[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39b98a36", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: get most recent file\n", + "list_of_files = glob.glob(f\"{datastream_dir}/*\") # * means all if need specific format then *.csv\n", + "latest_file = max(list_of_files, key=os.path.getctime)\n", + "latest_file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "902d514e", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus sort datastream files based on datetime\n", + "files = os.listdir(datastream_dir)\n", + "file_sorted = files.copy()\n", + "file_sorted.sort() \n", + "print(file_sorted[:5])\n", + "\n", + "# to reverse\n", + "file_sorted_reverse = files.copy() \n", + "file_sorted_reverse.sort(reverse=True)\n", + "print(file_sorted_reverse[:5])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec5923b2", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: pattern matching\n", + "# filter the 200709** files under datastream_dir\n", + "files_filter = glob.glob(f'{datastream_dir}/*.200709*.*')\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c4a4aa18", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: use armnotebook_utils.file_filter (TODO) to filter files based on datastream info \n" + ] + }, + { + "cell_type": "markdown", + "id": "cfeb9efc", + "metadata": {}, + "source": [ + "### Load data\n", + "The arm data is stored in [NetCDF](https://en.wikipedia.org/wiki/NetCDF#:~:text=%22NetCDF%20(network%20Common%20Data%20Form,format%20for%20representing%20scientific%20data.) format. We can use xarray's `open_dataset` method to load single file, or `open_mfdataset` to open multiple files. (Note: the latter will still return a single xarray dataset object by combining multiple files.)\n", + "\n", + "Note: open_dataset keeps the file handle open and lazy loads its contents. All parameters are passed directly to open_dataset. It is a preferable method over load_dataset for memory-effiency. (more details, please see [xarray.open_dataset](https://docs.xarray.dev/en/stable/generated/xarray.open_dataset.html))\n", + "\n", + "See the following example in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a440a329", + "metadata": {}, + "outputs": [], + "source": [ + "# open single file\n", + "full_path = latest_file\n", + "print(full_path)\n", + "ds_single = xr.open_dataset(full_path)\n", + "ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0143a3d", + "metadata": {}, + "outputs": [], + "source": [ + "print(ds_single)\n", + "print(type(ds_single))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c0f8939", + "metadata": {}, + "outputs": [], + "source": [ + "# open multiple files\n", + "n_files = 3\n", + "full_paths = [os.path.join(datastream_dir, f_path) for f_path in file_sorted_reverse[:n_files]]\n", + "print(full_paths)\n", + "try: # Note: sometimes multiple files cannot be merged, so we used try except here.\n", + " ds_mutiple = xr.open_mfdataset(full_paths)\n", + " # ds_mutiple\n", + " print(type(ds_mutiple))\n", + " print(ds_mutiple)\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "3fa59943", + "metadata": {}, + "source": [ + "## Explore the data" + ] + }, + { + "cell_type": "markdown", + "id": "571f69b9", + "metadata": {}, + "source": [ + "### NetCDF Data structure \n", + "\n", + "\n", + "(If you are confident with NetCDF basics and xarray essentials, feel free to skip this session.)\n", + "Before we dive into data exploration, there are some eseential concpets we should be familiar with (shown below): \n", + "* Dataset\n", + "* Data array\n", + "* Variable\n", + "* Dimenssion\n", + "* Coordinate\n", + "* Data Type\n", + "* Meta Data (Attributes)\n", + "\n", + "We will not go into details about NetCDF basics and here are some references you might find helpful\n", + "* [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#)\n", + "* [Network Common Data Form (NetCDF)](https://www.unidata.ucar.edu/software/netcdf/) and [A Brief History of (netCDF) Time](https://www.unidata.ucar.edu/software/netcdf/time/recs.html)\n", + "\n", + "\n", + "\n", + "### Xarray essentials\n", + "Earlier we introduced the xarray pacakge and used `open_dataset` and `open_mfdataset` to retrieve NetCDF data file as an xarray Dataset object. Recall that you can review the data in a notebook by using `print(ds)` or `ds`. \n", + "\n", + "Using xarray to retrieve the aforementioned NetCDF basics is straightforwared. In general,\n", + "* Dataset: `ds`\n", + "* Data array: `ds.variables`\n", + "* Variable: `ds.ds.variables`\n", + "* Dimenssion: `ds.dims`\n", + "* Coordinate: `ds.coords`\n", + "* Data Type: `type`\n", + "* Meta Data (Attributes)\n", + "\n", + "Also, here are some references if you are new to xarray\n", + "* [Xarray in 45 minutes](https://tutorial.xarray.dev/overview/xarray-in-45-min.html)\n", + "* [Handling NetCDF Files using XArray for Absolute Beginners](https://towardsdatascience.com/handling-netcdf-files-using-xarray-for-absolute-beginners-111a8ab4463f)\n", + "\n", + "Try the following commands in action\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b3eac323", + "metadata": {}, + "outputs": [], + "source": [ + "ds = ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77ecf85d", + "metadata": {}, + "outputs": [], + "source": [ + "# Dataset \n", + "ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25e7de09", + "metadata": {}, + "outputs": [], + "source": [ + "# Data array, Variable\n", + "# Note: the info can be overwhelming. Do not attempt to grasp everything at the first glance.\n", + "ds.variables" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c41b67e", + "metadata": {}, + "outputs": [], + "source": [ + "# dimenssions\n", + "ds.dims" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "156f1dfc", + "metadata": {}, + "outputs": [], + "source": [ + "# coordinates\n", + "ds.coords" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "277d6064", + "metadata": {}, + "outputs": [], + "source": [ + "# Meta Data (Attributes)\n", + "ds.attrs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d334681f", + "metadata": {}, + "outputs": [], + "source": [ + "# type\n", + "print(type(ds))\n", + "print(type(ds.variables))\n", + "print(type(ds.dims))\n", + "print(type(ds.coords))\n", + "print(type(ds.attrs))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "643399d6", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: retrieve variable names only\n", + "list(ds.variables)" + ] + }, + { + "cell_type": "markdown", + "id": "1cf36878", + "metadata": {}, + "source": [ + "#### Discussion: variable vs. coordinates vs. dimenssions. \n", + "From [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#):\n", + "\n", + "* Dimession: A dimension may be used to represent a real physical dimension, for example, time, latitude, longitude, or height. A dimension might also be used to index other quantities, for example station or model-run-number.\n", + "A netCDF dimension has both a name and a length. A dimension length is an arbitrary positive integer, except that one dimension in a netCDF dataset can have the length UNLIMITED.\n", + "\n", + "* Variables: Variables are used to store the bulk of the data in a netCDF dataset. A variable represents an array of values of the same type. A scalar value is treated as a 0-dimensional array. A variable has a name, a data type, and a shape described by its list of dimensions specified when the variable is created. A variable may also have associated attributes, which may be added, deleted or changed after the variable is created.\n", + "\n", + "* Coordinate (Variables): It is legal for a variable to have the same name as a dimension. Such variables have no special meaning to the netCDF library. However there is a convention that such variables should be treated in a special way by software using this library.\n", + "A variable with the same name as a dimension is called a coordinate variable. It typically defines a physical coordinate corresponding to that dimension.\n", + "\n", + "\n", + "Tips: By definition, coordinates and dimenssions are also variable. For example ds.time is a coordinate but it is also a special variable. But in practice, when we talk about variable, it implies regular/non-coordinate variable. We will use this convention for the remaining notebook. " + ] + }, + { + "cell_type": "markdown", + "id": "d47120f9", + "metadata": {}, + "source": [ + "### Xarray Variable\n", + "\n", + "\n", + "We can use `ds.variables` to access varialbes of a dataset. But many times viewing print-out the whole datasets can be overwhemling. Instead, we would work on the individual variable (also called Xarray data array.) using the `ds[var_name]` syntax, where `var_name` is the name of the variable. See the following examples in action.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5a57e136", + "metadata": {}, + "outputs": [], + "source": [ + "print(type(ds[\"time\"]))\n", + "ds[\"time\"]" + ] + }, + { + "cell_type": "markdown", + "id": "51ec643e", + "metadata": {}, + "source": [ + "#### Variable properties\n", + "For individual varible, the following properties are mostly used with the assocaited xarray method to retrieve them. (assuming var = ds[var_name])\n", + "* name: `var.name`\n", + "* data content: `var.data`\n", + "* attributes: `var.attrs`\n", + "* dimenstions: `var.dims`\n", + "* data type: `var.data.dtype`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a632a525", + "metadata": {}, + "outputs": [], + "source": [ + "var_name = \"time\"\n", + "var = ds[var_name]\n", + "\n", + "print(\"var.name: \\n\", var.name, \"\\n\")\n", + "print(\"var.data: \\n\", var.data, \"\\n\")\n", + "print(\"var.attrs: \\n\", var.attrs, \"\\n\")\n", + "print(\"var.dims: \\n\", var.dims, \"\\n\")\n", + "print(\"var.data.dtype: \\n\", var.data.dtype, \"\\n\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b256b07f", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: indivdual variable info table (collect and display variable info as a tabular format)\n", + "df_info = pd.DataFrame()\n", + "df_info[\"var_name\"] = list(ds.variables)\n", + "df_info[\"dims\"] = df_info.var_name.apply(lambda x: ds[x].dims)\n", + "df_info[\"is_dim\"] = df_info.var_name.apply(lambda x: x in ds.dims)\n", + "df_info[\"n_dim\"] = df_info.var_name.apply(lambda x: len(ds[x].dims))\n", + "df_info[\"attrs\"] = df_info.var_name.apply(lambda x: ds[x].attrs)\n", + "df_info[\"dtype\"] = df_info.var_name.apply(lambda x: ds[x].data.dtype)\n", + "df_info" + ] + }, + { + "cell_type": "markdown", + "id": "e6a36b96", + "metadata": {}, + "source": [ + "### Data cleaning/Preprocessing (skipped)\n", + "Data cleaning and preprocessing is an important stage in the the data analysis pipeline. However it will be out of the scope of this notebook. For more information about data cleaning and preprocessing basics, here are some references. \n", + "* [Xarray Fundamentals](https://earth-env-data-science.github.io/lectures/xarray/xarray.html)\n", + "* [Xarray Tutorial — Pangeo Gallery documentation](http://gallery.pangeo.io/repos/pangeo-data/pangeo-tutorial-gallery/xarray.html)\n", + "* [Pythonic Data Cleaning With pandas and NumPy](https://realpython.com/python-data-cleaning-numpy-pandas/)\n", + "* [Pandas - Cleaning Data](https://www.w3schools.com/python/pandas/pandas_cleaning.asp)" + ] + }, + { + "cell_type": "markdown", + "id": "ae6e1a69", + "metadata": {}, + "source": [ + "### Plotting\n", + "Data visualization (or plotting) is another important data analysis topic and deserves its own discussion. Here in this notebook we will only demonstrate basic tool for simple data visualization tasks.\n", + "\n", + "Here are some reference you might find useful:\n", + "* xarray plotting: https://docs.xarray.dev/en/stable/user-guide/plotting.html\n", + "* Atmospheric Community Toolkit (ACT): https://arm-doe.github.io/ACT/index.html\n", + "\n", + "Note: this notebook is auto-generated using a template. It uses a general idea to select variable(s) to plot and is not customized to each indivdual notebook. Feel free to change the variables in intrrests, especially certain figure is failed to plot." + ] + }, + { + "cell_type": "markdown", + "id": "7e417fb2", + "metadata": {}, + "source": [ + "#### 1-dimenssional basic time series plot\n", + "\n", + "For the following plot we would like to find variables such that\n", + "* it has one and only one dimession\n", + "* \"time\" is its coordinate variable\n", + "* it is not a dimenssion itself,\n", + "* it is not a special variable with substrings within [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "\n", + "Please see the following example in action. (For more details about pandas filtering, please see the following references.)\n", + "* [pandas: multiple conditions while indexing data frame](https://stackoverflow.com/questions/22591174/pandas-multiple-conditions-while-indexing-data-frame-unexpected-behavior)\n", + "* [pandas.DataFrame.apply](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.apply.html)\n", + "* [How to test if a string contains one of the substrings in a list...](https://stackoverflow.com/questions/26577516/how-to-test-if-a-string-contains-one-of-the-substrings-in-a-list-in-pandas" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30edac2d", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==1, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter = df_info[(df_info.n_dim == 1) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4777a659", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_1d = df_filter.var_name.values[0]\n", + " var_1d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb5ae985", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " ds[var_1d].plot()\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "9e41066e", + "metadata": {}, + "source": [ + "#### 2-dimenssional basic plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09bd4adc", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==2, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_2 = df_info[(df_info.n_dim == 2) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2cdea1a9", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_2d = df_filter_2.var_name.values[0]\n", + " var_2d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5f48879", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " print(ds[var_2d].dims)\n", + " # ds[var_2d].plot()\n", + "\n", + " # conventionally, use \"time\" as x-axis\n", + " ds[var_2d].plot(x=\"time\")\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "7554b2a6", + "metadata": {}, + "source": [ + "#### qc-plotting (optional)\n", + "\n", + "Note: act qc plotting has more strict requirement, one of them is the associated qc variable need to have \"flag_masks\" attributes. Which is added by using ds = act.io.armfiles.read_netcdf(files_list), then ds.clean.cleanup()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71c2096f", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " ds_act = act.io.armfiles.read_netcdf(full_path)\n", + " print(type(ds_act))\n", + " ds_act.clean.cleanup()\n", + "\n", + " # or \n", + " # ds.clean.cleanup()\n", + "except Exception as e:\n", + " print(\"ERROR\", e)\n", + " ds_act = ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "844f8505", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter valid variables for ACT qc plotting\n", + "condition = (\"qc_\" + df_info.var_name).apply(lambda x: ds_act[x].attrs.get(\"flag_masks\") is not None \n", + " if x in list(ds.variables) else False)\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_3 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings))) &\n", + " condition\n", + " ]\n", + "df_filter_3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd38cc6", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "try:\n", + " qc_variable = df_filter_3.var_name.values[0]\n", + " print(qc_variable)\n", + "except Exception as e:\n", + " print(e)\n", + " \n", + "try:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + " qc_ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "d76e4d27", + "metadata": {}, + "source": [ + "#### bonus: choose variables to plot from a dropdown menu " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb733804", + "metadata": {}, + "outputs": [], + "source": [ + "# Valid variables filtering\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_4 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82d5c20d", + "metadata": {}, + "outputs": [], + "source": [ + "# example 1: using xarray plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2fc9aaa4", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# fig, ax = plt.subplots(figsize=(10, 4))\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "# fig.clear() # Remove old lines from plot and plot new one\n", + "# if len(ds[var].dims)==2:\n", + "# ds[var].plot(x=\"time\", add_colorbar=False)\n", + "# else:\n", + "# ds[var].plot()\n", + "# plt.grid()\n", + "# plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fabdd802", + "metadata": {}, + "outputs": [], + "source": [ + "# example 2: using act plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "962b4186", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "\n", + "\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "\n", + "# i_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + "# i_display.add_subplots((1,), figsize=(10, 4))\n", + "# ax = i_display.plot(var, subplot_index=(0,), set_title=f\"{var} ({ds_act[var].attrs['long_name']})\",)\n", + "\n", + "# ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "# ax.grid()\n", + "# plt.show()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/SPHOTCOD/sphotcod2chiu.c1.ipynb b/VAPs/quicklook/SPHOTCOD/sphotcod2chiu.c1.ipynb new file mode 100644 index 00000000..54d31aa7 --- /dev/null +++ b/VAPs/quicklook/SPHOTCOD/sphotcod2chiu.c1.ipynb @@ -0,0 +1,297 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# SPHOTCOD2CHIU.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/sphotcod) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'sphotcod2chiu'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2019-12-31', 'facility': 'C1', 'site': 'ena', 'start_date': '2014-01-02'}, {'end_date': '2021-05-31', 'facility': 'C1', 'site': 'sgp', 'start_date': '2014-01-01'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2021-05-29'\n", + "date_end = '2021-05-31'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['cloud_optical_depth', 'liquid_water_path', 'effective_radius']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'cloud_optical_depth'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/SURFSPECALB/SURFSPECALB_tutorial.ipynb b/VAPs/quicklook/SURFSPECALB/SURFSPECALB_tutorial.ipynb new file mode 100644 index 00000000..e0e12e11 --- /dev/null +++ b/VAPs/quicklook/SURFSPECALB/SURFSPECALB_tutorial.ipynb @@ -0,0 +1,838 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# SURFSPECALB1MLAWER.C1 Notebook\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/surfspecalb) for more information about this vap." + ] + }, + { + "cell_type": "markdown", + "id": "97097763", + "metadata": {}, + "source": [ + "In this notebook, we demonstrate the workflow to explore ARM vap data (using surfspecalb1mlawer as an example.) Value-added products (VAPs) are higher-order data products that have been analyzed and processed to ease scientists’ use of ARM data in atmospheric research and global climate models. \n", + "Here is the main content we will cover." + ] + }, + { + "cell_type": "markdown", + "id": "eddec40f", + "metadata": {}, + "source": [ + "# Table of Content\n", + "## Access the data\n", + "* How to retrieve the data\n", + "* Data path and file name conventions\n", + "* Load data\n", + "## Explore the data\n", + "* NetCDF Data structure\n", + "* Xarray essentials\n", + "* Xarray Variable\n", + "## Plot the data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "import random\n", + "\n", + "import glob\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "2372d5be", + "metadata": {}, + "source": [ + "## Access the data" + ] + }, + { + "cell_type": "markdown", + "id": "5b0d9684", + "metadata": {}, + "source": [ + "### How to retrieve the data\n", + "We assume the path \"/data/archive\" is available where you are running this notebook. You can use `os.path.exists(\"/data/archive\")` to verify if the path exists at your machine.\n", + "\n", + "\n", + "### Data path and file name conventions\n", + "There several common terminologies regarding ARM data, for example, data-stream-name, data-level, site, facility, instrument, etc. (For more details, please see [ARM Data File Standards Version 1.2](https://www.google.com/search?q=arm+datastream+facility+cite+definition&rlz=1C1GCEJ_enUS1029US1029&ei=hb41ZICrBPukqtsPvMODIA&ved=0ahUKEwjAgeiS0aL-AhV7kmoFHbzhAAQQ4dUDCBA&uact=5&oq=arm+datastream+facility+cite+definition&gs_lcp=Cgxnd3Mtd2l6LXNlcnAQAzIFCAAQogQyBQgAEKIEMgUIABCiBDIFCAAQogQyBQgAEKIEOgoIABBHENYEELADOgoIIRCgARDDBBAKSgQIQRgAUJ4DWIoOYO0PaAFwAXgAgAGMAYgBuQmSAQMyLjmYAQCgAQHIAQjAAQE&sclient=gws-wiz-serp).) \n", + "\n", + "For example, this notebook is called `surfspecalb1mlawer.c1`, where `surfspecalb1mlawer` is the \"datastream name\", and the `{process.ds_class_level}}` is the \"data level\".\n", + "\n", + "This datastream also contains site `nsa` and facility `C1`. (Note: individual datastream might have multiple site-facility pairs.)\n", + "In such a case, the data of this data-stream is stored at `/data/archive/nsa/nsasurfspecalb1mlawerC1.c1`, which is in the format of `//.`. We can use the following method to assign the data-stream directory `datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )`\n", + "\n", + "The data files under datastream_dir also follows naming conventions. But once reach the datastream_dir level, the most import file naming convention to differentiate the files is \"yyyyMMdd.hhmmss\", which comes handy to filter out files based on datetime. For example, we can use `glob.glob(f'{datastream_dir}/*.200709*.*')` to filter files in 2007 September.\n", + "\n", + "(Note: refer to the https://adc.arm.gov/discovery/#/ and https://adc.arm.gov/solr8/metadata/select API to explore ARM datastream and assoicated available sites and facilities\n", + "\n", + "Please see the following examples in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d7e9eb85", + "metadata": {}, + "outputs": [], + "source": [ + "# Verify if DATA_DIR path exists\n", + "DATA_DIR = \"/data/archive\"\n", + "os.path.exists(DATA_DIR)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "586993fd", + "metadata": {}, + "outputs": [], + "source": [ + "# Speicify datastream_dir following the path conventions and check its existence\n", + "DATASTREAM_NAME = \"surfspecalb1mlawer\"\n", + "DATA_LEVEL = \"c1\"\n", + "site = \"nsa\"\n", + "facility = \"C1\"\n", + "datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )\n", + "print(datastream_dir)\n", + "print(os.path.exists(datastream_dir))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0742f7c1", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: list 5 (random) files under datastream_dir\n", + "files = os.listdir(datastream_dir)\n", + "files[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39b98a36", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: get most recent file\n", + "list_of_files = glob.glob(f\"{datastream_dir}/*\") # * means all if need specific format then *.csv\n", + "latest_file = max(list_of_files, key=os.path.getctime)\n", + "latest_file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "902d514e", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus sort datastream files based on datetime\n", + "files = os.listdir(datastream_dir)\n", + "file_sorted = files.copy()\n", + "file_sorted.sort() \n", + "print(file_sorted[:5])\n", + "\n", + "# to reverse\n", + "file_sorted_reverse = files.copy() \n", + "file_sorted_reverse.sort(reverse=True)\n", + "print(file_sorted_reverse[:5])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec5923b2", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: pattern matching\n", + "# filter the 200709** files under datastream_dir\n", + "files_filter = glob.glob(f'{datastream_dir}/*.200709*.*')\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c4a4aa18", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: use armnotebook_utils.file_filter (TODO) to filter files based on datastream info \n" + ] + }, + { + "cell_type": "markdown", + "id": "cfeb9efc", + "metadata": {}, + "source": [ + "### Load data\n", + "The arm data is stored in [NetCDF](https://en.wikipedia.org/wiki/NetCDF#:~:text=%22NetCDF%20(network%20Common%20Data%20Form,format%20for%20representing%20scientific%20data.) format. We can use xarray's `open_dataset` method to load single file, or `open_mfdataset` to open multiple files. (Note: the latter will still return a single xarray dataset object by combining multiple files.)\n", + "\n", + "Note: open_dataset keeps the file handle open and lazy loads its contents. All parameters are passed directly to open_dataset. It is a preferable method over load_dataset for memory-effiency. (more details, please see [xarray.open_dataset](https://docs.xarray.dev/en/stable/generated/xarray.open_dataset.html))\n", + "\n", + "See the following example in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a440a329", + "metadata": {}, + "outputs": [], + "source": [ + "# open single file\n", + "full_path = latest_file\n", + "print(full_path)\n", + "ds_single = xr.open_dataset(full_path)\n", + "ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0143a3d", + "metadata": {}, + "outputs": [], + "source": [ + "print(ds_single)\n", + "print(type(ds_single))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c0f8939", + "metadata": {}, + "outputs": [], + "source": [ + "# open multiple files\n", + "n_files = 3\n", + "full_paths = [os.path.join(datastream_dir, f_path) for f_path in file_sorted_reverse[:n_files]]\n", + "print(full_paths)\n", + "try: # Note: sometimes multiple files cannot be merged, so we used try except here.\n", + " ds_mutiple = xr.open_mfdataset(full_paths)\n", + " # ds_mutiple\n", + " print(type(ds_mutiple))\n", + " print(ds_mutiple)\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "3fa59943", + "metadata": {}, + "source": [ + "## Explore the data" + ] + }, + { + "cell_type": "markdown", + "id": "571f69b9", + "metadata": {}, + "source": [ + "### NetCDF Data structure \n", + "\n", + "\n", + "(If you are confident with NetCDF basics and xarray essentials, feel free to skip this session.)\n", + "Before we dive into data exploration, there are some eseential concpets we should be familiar with (shown below): \n", + "* Dataset\n", + "* Data array\n", + "* Variable\n", + "* Dimenssion\n", + "* Coordinate\n", + "* Data Type\n", + "* Meta Data (Attributes)\n", + "\n", + "We will not go into details about NetCDF basics and here are some references you might find helpful\n", + "* [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#)\n", + "* [Network Common Data Form (NetCDF)](https://www.unidata.ucar.edu/software/netcdf/) and [A Brief History of (netCDF) Time](https://www.unidata.ucar.edu/software/netcdf/time/recs.html)\n", + "\n", + "\n", + "\n", + "### Xarray essentials\n", + "Earlier we introduced the xarray pacakge and used `open_dataset` and `open_mfdataset` to retrieve NetCDF data file as an xarray Dataset object. Recall that you can review the data in a notebook by using `print(ds)` or `ds`. \n", + "\n", + "Using xarray to retrieve the aforementioned NetCDF basics is straightforwared. In general,\n", + "* Dataset: `ds`\n", + "* Data array: `ds.variables`\n", + "* Variable: `ds.ds.variables`\n", + "* Dimenssion: `ds.dims`\n", + "* Coordinate: `ds.coords`\n", + "* Data Type: `type`\n", + "* Meta Data (Attributes)\n", + "\n", + "Also, here are some references if you are new to xarray\n", + "* [Xarray in 45 minutes](https://tutorial.xarray.dev/overview/xarray-in-45-min.html)\n", + "* [Handling NetCDF Files using XArray for Absolute Beginners](https://towardsdatascience.com/handling-netcdf-files-using-xarray-for-absolute-beginners-111a8ab4463f)\n", + "\n", + "Try the following commands in action\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b3eac323", + "metadata": {}, + "outputs": [], + "source": [ + "ds = ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77ecf85d", + "metadata": {}, + "outputs": [], + "source": [ + "# Dataset \n", + "ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25e7de09", + "metadata": {}, + "outputs": [], + "source": [ + "# Data array, Variable\n", + "# Note: the info can be overwhelming. Do not attempt to grasp everything at the first glance.\n", + "ds.variables" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c41b67e", + "metadata": {}, + "outputs": [], + "source": [ + "# dimenssions\n", + "ds.dims" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "156f1dfc", + "metadata": {}, + "outputs": [], + "source": [ + "# coordinates\n", + "ds.coords" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "277d6064", + "metadata": {}, + "outputs": [], + "source": [ + "# Meta Data (Attributes)\n", + "ds.attrs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d334681f", + "metadata": {}, + "outputs": [], + "source": [ + "# type\n", + "print(type(ds))\n", + "print(type(ds.variables))\n", + "print(type(ds.dims))\n", + "print(type(ds.coords))\n", + "print(type(ds.attrs))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "643399d6", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: retrieve variable names only\n", + "list(ds.variables)" + ] + }, + { + "cell_type": "markdown", + "id": "1cf36878", + "metadata": {}, + "source": [ + "#### Discussion: variable vs. coordinates vs. dimenssions. \n", + "From [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#):\n", + "\n", + "* Dimession: A dimension may be used to represent a real physical dimension, for example, time, latitude, longitude, or height. A dimension might also be used to index other quantities, for example station or model-run-number.\n", + "A netCDF dimension has both a name and a length. A dimension length is an arbitrary positive integer, except that one dimension in a netCDF dataset can have the length UNLIMITED.\n", + "\n", + "* Variables: Variables are used to store the bulk of the data in a netCDF dataset. A variable represents an array of values of the same type. A scalar value is treated as a 0-dimensional array. A variable has a name, a data type, and a shape described by its list of dimensions specified when the variable is created. A variable may also have associated attributes, which may be added, deleted or changed after the variable is created.\n", + "\n", + "* Coordinate (Variables): It is legal for a variable to have the same name as a dimension. Such variables have no special meaning to the netCDF library. However there is a convention that such variables should be treated in a special way by software using this library.\n", + "A variable with the same name as a dimension is called a coordinate variable. It typically defines a physical coordinate corresponding to that dimension.\n", + "\n", + "\n", + "Tips: By definition, coordinates and dimenssions are also variable. For example ds.time is a coordinate but it is also a special variable. But in practice, when we talk about variable, it implies regular/non-coordinate variable. We will use this convention for the remaining notebook. " + ] + }, + { + "cell_type": "markdown", + "id": "d47120f9", + "metadata": {}, + "source": [ + "### Xarray Variable\n", + "\n", + "\n", + "We can use `ds.variables` to access varialbes of a dataset. But many times viewing print-out the whole datasets can be overwhemling. Instead, we would work on the individual variable (also called Xarray data array.) using the `ds[var_name]` syntax, where `var_name` is the name of the variable. See the following examples in action.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5a57e136", + "metadata": {}, + "outputs": [], + "source": [ + "print(type(ds[\"time\"]))\n", + "ds[\"time\"]" + ] + }, + { + "cell_type": "markdown", + "id": "51ec643e", + "metadata": {}, + "source": [ + "#### Variable properties\n", + "For individual varible, the following properties are mostly used with the assocaited xarray method to retrieve them. (assuming var = ds[var_name])\n", + "* name: `var.name`\n", + "* data content: `var.data`\n", + "* attributes: `var.attrs`\n", + "* dimenstions: `var.dims`\n", + "* data type: `var.data.dtype`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a632a525", + "metadata": {}, + "outputs": [], + "source": [ + "var_name = \"time\"\n", + "var = ds[var_name]\n", + "\n", + "print(\"var.name: \\n\", var.name, \"\\n\")\n", + "print(\"var.data: \\n\", var.data, \"\\n\")\n", + "print(\"var.attrs: \\n\", var.attrs, \"\\n\")\n", + "print(\"var.dims: \\n\", var.dims, \"\\n\")\n", + "print(\"var.data.dtype: \\n\", var.data.dtype, \"\\n\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b256b07f", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: indivdual variable info table (collect and display variable info as a tabular format)\n", + "df_info = pd.DataFrame()\n", + "df_info[\"var_name\"] = list(ds.variables)\n", + "df_info[\"dims\"] = df_info.var_name.apply(lambda x: ds[x].dims)\n", + "df_info[\"is_dim\"] = df_info.var_name.apply(lambda x: x in ds.dims)\n", + "df_info[\"n_dim\"] = df_info.var_name.apply(lambda x: len(ds[x].dims))\n", + "df_info[\"attrs\"] = df_info.var_name.apply(lambda x: ds[x].attrs)\n", + "df_info[\"dtype\"] = df_info.var_name.apply(lambda x: ds[x].data.dtype)\n", + "df_info" + ] + }, + { + "cell_type": "markdown", + "id": "e6a36b96", + "metadata": {}, + "source": [ + "### Data cleaning/Preprocessing (skipped)\n", + "Data cleaning and preprocessing is an important stage in the the data analysis pipeline. However it will be out of the scope of this notebook. For more information about data cleaning and preprocessing basics, here are some references. \n", + "* [Xarray Fundamentals](https://earth-env-data-science.github.io/lectures/xarray/xarray.html)\n", + "* [Xarray Tutorial — Pangeo Gallery documentation](http://gallery.pangeo.io/repos/pangeo-data/pangeo-tutorial-gallery/xarray.html)\n", + "* [Pythonic Data Cleaning With pandas and NumPy](https://realpython.com/python-data-cleaning-numpy-pandas/)\n", + "* [Pandas - Cleaning Data](https://www.w3schools.com/python/pandas/pandas_cleaning.asp)" + ] + }, + { + "cell_type": "markdown", + "id": "ae6e1a69", + "metadata": {}, + "source": [ + "### Plotting\n", + "Data visualization (or plotting) is another important data analysis topic and deserves its own discussion. Here in this notebook we will only demonstrate basic tool for simple data visualization tasks.\n", + "\n", + "Here are some reference you might find useful:\n", + "* xarray plotting: https://docs.xarray.dev/en/stable/user-guide/plotting.html\n", + "* Atmospheric Community Toolkit (ACT): https://arm-doe.github.io/ACT/index.html\n", + "\n", + "Note: this notebook is auto-generated using a template. It uses a general idea to select variable(s) to plot and is not customized to each indivdual notebook. Feel free to change the variables in intrrests, especially certain figure is failed to plot." + ] + }, + { + "cell_type": "markdown", + "id": "7e417fb2", + "metadata": {}, + "source": [ + "#### 1-dimenssional basic time series plot\n", + "\n", + "For the following plot we would like to find variables such that\n", + "* it has one and only one dimession\n", + "* \"time\" is its coordinate variable\n", + "* it is not a dimenssion itself,\n", + "* it is not a special variable with substrings within [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "\n", + "Please see the following example in action. (For more details about pandas filtering, please see the following references.)\n", + "* [pandas: multiple conditions while indexing data frame](https://stackoverflow.com/questions/22591174/pandas-multiple-conditions-while-indexing-data-frame-unexpected-behavior)\n", + "* [pandas.DataFrame.apply](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.apply.html)\n", + "* [How to test if a string contains one of the substrings in a list...](https://stackoverflow.com/questions/26577516/how-to-test-if-a-string-contains-one-of-the-substrings-in-a-list-in-pandas" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30edac2d", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==1, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter = df_info[(df_info.n_dim == 1) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4777a659", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_1d = df_filter.var_name.values[0]\n", + " var_1d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb5ae985", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " ds[var_1d].plot()\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "9e41066e", + "metadata": {}, + "source": [ + "#### 2-dimenssional basic plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09bd4adc", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==2, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_2 = df_info[(df_info.n_dim == 2) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2cdea1a9", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_2d = df_filter_2.var_name.values[0]\n", + " var_2d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5f48879", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " print(ds[var_2d].dims)\n", + " # ds[var_2d].plot()\n", + "\n", + " # conventionally, use \"time\" as x-axis\n", + " ds[var_2d].plot(x=\"time\")\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "7554b2a6", + "metadata": {}, + "source": [ + "#### qc-plotting (optional)\n", + "\n", + "Note: act qc plotting has more strict requirement, one of them is the associated qc variable need to have \"flag_masks\" attributes. Which is added by using ds = act.io.armfiles.read_netcdf(files_list), then ds.clean.cleanup()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71c2096f", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " ds_act = act.io.armfiles.read_netcdf(full_path)\n", + " print(type(ds_act))\n", + " ds_act.clean.cleanup()\n", + "\n", + " # or \n", + " # ds.clean.cleanup()\n", + "except Exception as e:\n", + " print(\"ERROR\", e)\n", + " ds_act = ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "844f8505", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter valid variables for ACT qc plotting\n", + "condition = (\"qc_\" + df_info.var_name).apply(lambda x: ds_act[x].attrs.get(\"flag_masks\") is not None \n", + " if x in list(ds.variables) else False)\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_3 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings))) &\n", + " condition\n", + " ]\n", + "df_filter_3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd38cc6", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "try:\n", + " qc_variable = df_filter_3.var_name.values[0]\n", + " print(qc_variable)\n", + "except Exception as e:\n", + " print(e)\n", + " \n", + "try:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + " qc_ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "d76e4d27", + "metadata": {}, + "source": [ + "#### bonus: choose variables to plot from a dropdown menu " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb733804", + "metadata": {}, + "outputs": [], + "source": [ + "# Valid variables filtering\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_4 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82d5c20d", + "metadata": {}, + "outputs": [], + "source": [ + "# example 1: using xarray plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2fc9aaa4", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# fig, ax = plt.subplots(figsize=(10, 4))\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "# fig.clear() # Remove old lines from plot and plot new one\n", + "# if len(ds[var].dims)==2:\n", + "# ds[var].plot(x=\"time\", add_colorbar=False)\n", + "# else:\n", + "# ds[var].plot()\n", + "# plt.grid()\n", + "# plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fabdd802", + "metadata": {}, + "outputs": [], + "source": [ + "# example 2: using act plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "962b4186", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "\n", + "\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "\n", + "# i_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + "# i_display.add_subplots((1,), figsize=(10, 4))\n", + "# ax = i_display.plot(var, subplot_index=(0,), set_title=f\"{var} ({ds_act[var].attrs['long_name']})\",)\n", + "\n", + "# ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "# ax.grid()\n", + "# plt.show()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/SURFSPECALB/surfspecalb1mlawer.c1.ipynb b/VAPs/quicklook/SURFSPECALB/surfspecalb1mlawer.c1.ipynb new file mode 100644 index 00000000..55d77626 --- /dev/null +++ b/VAPs/quicklook/SURFSPECALB/surfspecalb1mlawer.c1.ipynb @@ -0,0 +1,339 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# SURFSPECALB1MLAWER.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/surfspecalb) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'surfspecalb1mlawer'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2020-10-06', 'facility': 'C1', 'site': 'nsa', 'start_date': '1998-04-07'}, {'end_date': '2021-01-04', 'facility': 'C1', 'site': 'sgp', 'start_date': '2004-01-01'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2021-01-01'\n", + "date_end = '2021-01-03'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['be_surface_albedo_mfr_broadband_10m', 'be_surface_albedo_mfr_narrowband_10m', 'be_surface_albedo_psp_broadband_10m']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],force_line_plot=True)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'hemisp_broadband_mfrsrC1'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,force_line_plot=True)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'be_surface_albedo_mfr_broadband_10m'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],force_line_plot=True)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],force_line_plot=True)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/SURFSPECALB/surfspecalb7nch1mlawer.c1.ipynb b/VAPs/quicklook/SURFSPECALB/surfspecalb7nch1mlawer.c1.ipynb new file mode 100644 index 00000000..11e217f4 --- /dev/null +++ b/VAPs/quicklook/SURFSPECALB/surfspecalb7nch1mlawer.c1.ipynb @@ -0,0 +1,339 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# SURFSPECALB7NCH1MLAWER.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/surfspecalb) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'surfspecalb7nch1mlawer'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2023-08-17', 'facility': 'C1', 'site': 'nsa', 'start_date': '2021-06-23'}, {'end_date': '2023-08-01', 'facility': 'C1', 'site': 'sgp', 'start_date': '2021-01-13'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2023-07-29'\n", + "date_end = '2023-07-31'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['be_surface_albedo_mfr_narrowband_10m', 'be_surface_albedo_psp_broadband_10m', 'estimated_spectral_albedo_10m']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'hemisp_narrowband_mfrsrC1'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'be_surface_albedo_mfr_narrowband_10m'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/SWFLUXANAL/.ipynb_checkpoints/1swfanalsirs1long.c1-checkpoint.ipynb b/VAPs/quicklook/SWFLUXANAL/.ipynb_checkpoints/1swfanalsirs1long.c1-checkpoint.ipynb new file mode 100644 index 00000000..108d3647 --- /dev/null +++ b/VAPs/quicklook/SWFLUXANAL/.ipynb_checkpoints/1swfanalsirs1long.c1-checkpoint.ipynb @@ -0,0 +1,2654 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# 1SWFANALSIRS1LONG.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/swfluxanal) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = '1swfanalsirs1long'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2015-05-18', 'facility': 'C1', 'site': 'sgp', 'start_date': '1997-03-25'}, {'end_date': '2011-10-15', 'facility': 'E10', 'site': 'sgp', 'start_date': '1997-02-16'}, {'end_date': '2015-05-18', 'facility': 'E11', 'site': 'sgp', 'start_date': '1995-09-26'}, {'end_date': '2015-05-26', 'facility': 'E12', 'site': 'sgp', 'start_date': '1996-01-21'}, {'end_date': '2015-05-18', 'facility': 'E13', 'site': 'sgp', 'start_date': '1994-01-07'}, {'end_date': '2015-05-18', 'facility': 'E15', 'site': 'sgp', 'start_date': '1994-03-31'}, {'end_date': '2011-11-09', 'facility': 'E16', 'site': 'sgp', 'start_date': '1995-09-22'}, {'end_date': '2009-11-06', 'facility': 'E18', 'site': 'sgp', 'start_date': '1996-06-20'}, {'end_date': '2011-05-21', 'facility': 'E19', 'site': 'sgp', 'start_date': '1998-07-21'}, {'end_date': '2009-05-07', 'facility': 'E1', 'site': 'sgp', 'start_date': '1995-11-16'}, {'end_date': '2011-11-14', 'facility': 'E20', 'site': 'sgp', 'start_date': '1995-04-02'}, {'end_date': '2015-05-11', 'facility': 'E21', 'site': 'sgp', 'start_date': '1999-09-13'}, {'end_date': '2009-11-29', 'facility': 'E22', 'site': 'sgp', 'start_date': '1995-11-09'}, {'end_date': '2009-11-06', 'facility': 'E24', 'site': 'sgp', 'start_date': '1995-11-08'}, {'end_date': '2002-04-03', 'facility': 'E25', 'site': 'sgp', 'start_date': '1997-11-19'}, {'end_date': '2009-07-15', 'facility': 'E27', 'site': 'sgp', 'start_date': '2003-05-16'}, {'end_date': '2009-10-18', 'facility': 'E2', 'site': 'sgp', 'start_date': '1996-04-02'}, {'end_date': '2015-05-12', 'facility': 'E31', 'site': 'sgp', 'start_date': '2011-10-13'}, {'end_date': '2015-05-18', 'facility': 'E32', 'site': 'sgp', 'start_date': '2012-02-05'}, {'end_date': '2015-05-18', 'facility': 'E33', 'site': 'sgp', 'start_date': '2011-08-26'}, {'end_date': '2015-05-26', 'facility': 'E34', 'site': 'sgp', 'start_date': '2011-09-04'}, {'end_date': '2015-05-18', 'facility': 'E35', 'site': 'sgp', 'start_date': '2011-10-06'}, {'end_date': '2015-05-18', 'facility': 'E36', 'site': 'sgp', 'start_date': '2011-09-29'}, {'end_date': '2015-05-18', 'facility': 'E37', 'site': 'sgp', 'start_date': '2011-09-30'}, {'end_date': '2015-05-27', 'facility': 'E38', 'site': 'sgp', 'start_date': '2011-09-05'}, {'end_date': '2009-08-30', 'facility': 'E3', 'site': 'sgp', 'start_date': '1996-03-07'}, {'end_date': '2011-09-25', 'facility': 'E4', 'site': 'sgp', 'start_date': '1996-03-07'}, {'end_date': '2009-10-31', 'facility': 'E5', 'site': 'sgp', 'start_date': '1996-06-17'}, {'end_date': '2011-10-15', 'facility': 'E6', 'site': 'sgp', 'start_date': '1996-03-07'}, {'end_date': '2011-11-12', 'facility': 'E7', 'site': 'sgp', 'start_date': '1995-10-20'}, {'end_date': '2009-11-04', 'facility': 'E8', 'site': 'sgp', 'start_date': '1995-09-29'}, {'end_date': '2015-05-26', 'facility': 'E9', 'site': 'sgp', 'start_date': '1994-01-19'}]" + ] + }, + { + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "ac6764f5", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The following locations and date ranges are available for this VAP:\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
sitefacilitystart_dateend_date
0sgpC11997-03-252015-05-18
1sgpE101997-02-162011-10-15
2sgpE111995-09-262015-05-18
3sgpE121996-01-212015-05-26
4sgpE131994-01-072015-05-18
5sgpE151994-03-312015-05-18
6sgpE161995-09-222011-11-09
7sgpE181996-06-202009-11-06
8sgpE191998-07-212011-05-21
9sgpE11995-11-162009-05-07
10sgpE201995-04-022011-11-14
11sgpE211999-09-132015-05-11
12sgpE221995-11-092009-11-29
13sgpE241995-11-082009-11-06
14sgpE251997-11-192002-04-03
15sgpE272003-05-162009-07-15
16sgpE21996-04-022009-10-18
17sgpE312011-10-132015-05-12
18sgpE322012-02-052015-05-18
19sgpE332011-08-262015-05-18
20sgpE342011-09-042015-05-26
21sgpE352011-10-062015-05-18
22sgpE362011-09-292015-05-18
23sgpE372011-09-302015-05-18
24sgpE382011-09-052015-05-27
25sgpE31996-03-072009-08-30
26sgpE41996-03-072011-09-25
27sgpE51996-06-172009-10-31
28sgpE61996-03-072011-10-15
29sgpE71995-10-202011-11-12
30sgpE81995-09-292009-11-04
31sgpE91994-01-192015-05-26
\n", + "
" + ], + "text/plain": [ + " site facility start_date end_date\n", + "0 sgp C1 1997-03-25 2015-05-18\n", + "1 sgp E10 1997-02-16 2011-10-15\n", + "2 sgp E11 1995-09-26 2015-05-18\n", + "3 sgp E12 1996-01-21 2015-05-26\n", + "4 sgp E13 1994-01-07 2015-05-18\n", + "5 sgp E15 1994-03-31 2015-05-18\n", + "6 sgp E16 1995-09-22 2011-11-09\n", + "7 sgp E18 1996-06-20 2009-11-06\n", + "8 sgp E19 1998-07-21 2011-05-21\n", + "9 sgp E1 1995-11-16 2009-05-07\n", + "10 sgp E20 1995-04-02 2011-11-14\n", + "11 sgp E21 1999-09-13 2015-05-11\n", + "12 sgp E22 1995-11-09 2009-11-29\n", + "13 sgp E24 1995-11-08 2009-11-06\n", + "14 sgp E25 1997-11-19 2002-04-03\n", + "15 sgp E27 2003-05-16 2009-07-15\n", + "16 sgp E2 1996-04-02 2009-10-18\n", + "17 sgp E31 2011-10-13 2015-05-12\n", + "18 sgp E32 2012-02-05 2015-05-18\n", + "19 sgp E33 2011-08-26 2015-05-18\n", + "20 sgp E34 2011-09-04 2015-05-26\n", + "21 sgp E35 2011-10-06 2015-05-18\n", + "22 sgp E36 2011-09-29 2015-05-18\n", + "23 sgp E37 2011-09-30 2015-05-18\n", + "24 sgp E38 2011-09-05 2015-05-27\n", + "25 sgp E3 1996-03-07 2009-08-30\n", + "26 sgp E4 1996-03-07 2011-09-25\n", + "27 sgp E5 1996-06-17 2009-10-31\n", + "28 sgp E6 1996-03-07 2011-10-15\n", + "29 sgp E7 1995-10-20 2011-11-12\n", + "30 sgp E8 1995-09-29 2009-11-04\n", + "31 sgp E9 1994-01-19 2015-05-26" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2015-05-15'\n", + "date_end = '2015-05-17'" + ] + }, + { + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'/data/archive/sgp/sgp1swfanalsirs1longC1.c1'" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['20150515', '20150516', '20150517']" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "51feea2e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['/data/archive/sgp/sgp1swfanalsirs1longC1.c1/sgp1swfanalsirs1longC1.c1.20150515.112900.cdf',\n", + " '/data/archive/sgp/sgp1swfanalsirs1longC1.c1/sgp1swfanalsirs1longC1.c1.20150516.112800.cdf',\n", + " '/data/archive/sgp/sgp1swfanalsirs1longC1.c1/sgp1swfanalsirs1longC1.c1.20150517.112700.cdf']" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "89 files loaded\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
<xarray.Dataset>\n",
+       "Dimensions:                          (time: 838)\n",
+       "Coordinates:\n",
+       "  * time                             (time) timedelta64[ns] 00:00:00 ... 13:5...\n",
+       "Data variables: (12/47)\n",
+       "    base_time                        object ...\n",
+       "    time_offset                      (time) timedelta64[ns] dask.array<chunksize=(838,), meta=np.ndarray>\n",
+       "    base_time_LST                    object ...\n",
+       "    time_offset_LST                  (time) timedelta64[ns] dask.array<chunksize=(838,), meta=np.ndarray>\n",
+       "    site                             |S64 ...\n",
+       "    coef_date                        float64 ...\n",
+       "    ...                               ...\n",
+       "    qc_difswfluxdn                   (time) int16 dask.array<chunksize=(838,), meta=np.ndarray>\n",
+       "    qc_dirswfluxdn                   (time) int16 dask.array<chunksize=(838,), meta=np.ndarray>\n",
+       "    qc_sswfluxdn                     (time) int16 dask.array<chunksize=(838,), meta=np.ndarray>\n",
+       "    lat                              float32 ...\n",
+       "    lon                              float32 ...\n",
+       "    alt                              float32 ...\n",
+       "Attributes: (12/14)\n",
+       "    Date:                      Sat Jun 20 17:08:21 GMT 2015\n",
+       "    Fitmode:                   01\n",
+       "    Version:                   $State: vap-swfanal1long-3.12-0.sol5_10$\n",
+       "    Number_Input_Platforms:    1\n",
+       "    Input_Platforms:           sgpsirsC1.b1\n",
+       "    Input_Platforms_Versions:  /usr/lib/ld.so.1\n",
+       "    ...                        ...\n",
+       "    comment:                   fitmode=01 indicates a daily fit, fitmode=00 i...\n",
+       "    _file_dates:               ['20150515']\n",
+       "    _file_times:               ['112900']\n",
+       "    datastream:                sgp1swfanalsirs1longC1.c1\n",
+       "    _datastream:               sgp1swfanalsirs1longC1.c1\n",
+       "    _arm_standards_flag:       1
" + ], + "text/plain": [ + "\n", + "Dimensions: (time: 838)\n", + "Coordinates:\n", + " * time (time) timedelta64[ns] 00:00:00 ... 13:5...\n", + "Data variables: (12/47)\n", + " base_time object ...\n", + " time_offset (time) timedelta64[ns] dask.array\n", + " base_time_LST object ...\n", + " time_offset_LST (time) timedelta64[ns] dask.array\n", + " site |S64 ...\n", + " coef_date float64 ...\n", + " ... ...\n", + " qc_difswfluxdn (time) int16 dask.array\n", + " qc_dirswfluxdn (time) int16 dask.array\n", + " qc_sswfluxdn (time) int16 dask.array\n", + " lat float32 ...\n", + " lon float32 ...\n", + " alt float32 ...\n", + "Attributes: (12/14)\n", + " Date: Sat Jun 20 17:08:21 GMT 2015\n", + " Fitmode: 01\n", + " Version: $State: vap-swfanal1long-3.12-0.sol5_10$\n", + " Number_Input_Platforms: 1\n", + " Input_Platforms: sgpsirsC1.b1\n", + " Input_Platforms_Versions: /usr/lib/ld.so.1\n", + " ... ...\n", + " comment: fitmode=01 indicates a daily fit, fitmode=00 i...\n", + " _file_dates: ['20150515']\n", + " _file_times: ['112900']\n", + " datastream: sgp1swfanalsirs1longC1.c1\n", + " _datastream: sgp1swfanalsirs1longC1.c1\n", + " _arm_standards_flag: 1" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter [0]\n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['gswfluxdn_measured', 'gswfluxdn_clearskyfit', 'difswfluxdn_measured']" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/kefeimo/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/coding/variables.py:147: FutureWarning: elementwise comparison failed; returning scalar instead, but in the future will perform elementwise comparison\n", + " condition |= data == fv\n" + ] + }, + { + "ename": "OverflowError", + "evalue": "int too big to convert", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mOverflowError\u001b[0m Traceback (most recent call last)", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/IPython/core/formatters.py:972\u001b[0m, in \u001b[0;36mMimeBundleFormatter.__call__\u001b[0;34m(self, obj, include, exclude)\u001b[0m\n\u001b[1;32m 969\u001b[0m method \u001b[38;5;241m=\u001b[39m get_real_method(obj, \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mprint_method)\n\u001b[1;32m 971\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m method \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[0;32m--> 972\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mmethod\u001b[49m\u001b[43m(\u001b[49m\u001b[43minclude\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43minclude\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mexclude\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mexclude\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 973\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m\n\u001b[1;32m 974\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/ipympl/backend_nbagg.py:336\u001b[0m, in \u001b[0;36mCanvas._repr_mimebundle_\u001b[0;34m(self, **kwargs)\u001b[0m\n\u001b[1;32m 333\u001b[0m plaintext \u001b[38;5;241m=\u001b[39m plaintext[:\u001b[38;5;241m110\u001b[39m] \u001b[38;5;241m+\u001b[39m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124m…\u001b[39m\u001b[38;5;124m'\u001b[39m\n\u001b[1;32m 335\u001b[0m buf \u001b[38;5;241m=\u001b[39m io\u001b[38;5;241m.\u001b[39mBytesIO()\n\u001b[0;32m--> 336\u001b[0m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mfigure\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43msavefig\u001b[49m\u001b[43m(\u001b[49m\u001b[43mbuf\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43mformat\u001b[39;49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43mpng\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mdpi\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43mfigure\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m)\u001b[49m\n\u001b[1;32m 338\u001b[0m base64_image \u001b[38;5;241m=\u001b[39m b64encode(buf\u001b[38;5;241m.\u001b[39mgetvalue())\u001b[38;5;241m.\u001b[39mdecode(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mutf-8\u001b[39m\u001b[38;5;124m'\u001b[39m)\n\u001b[1;32m 339\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_data_url \u001b[38;5;241m=\u001b[39m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mdata:image/png;base64,\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mbase64_image\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m'\u001b[39m\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/matplotlib/figure.py:3343\u001b[0m, in \u001b[0;36mFigure.savefig\u001b[0;34m(self, fname, transparent, **kwargs)\u001b[0m\n\u001b[1;32m 3339\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m ax \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39maxes:\n\u001b[1;32m 3340\u001b[0m stack\u001b[38;5;241m.\u001b[39menter_context(\n\u001b[1;32m 3341\u001b[0m ax\u001b[38;5;241m.\u001b[39mpatch\u001b[38;5;241m.\u001b[39m_cm_set(facecolor\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mnone\u001b[39m\u001b[38;5;124m'\u001b[39m, edgecolor\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mnone\u001b[39m\u001b[38;5;124m'\u001b[39m))\n\u001b[0;32m-> 3343\u001b[0m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mcanvas\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mprint_figure\u001b[49m\u001b[43m(\u001b[49m\u001b[43mfname\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/matplotlib/backend_bases.py:2366\u001b[0m, in \u001b[0;36mFigureCanvasBase.print_figure\u001b[0;34m(self, filename, dpi, facecolor, edgecolor, orientation, format, bbox_inches, pad_inches, bbox_extra_artists, backend, **kwargs)\u001b[0m\n\u001b[1;32m 2362\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[1;32m 2363\u001b[0m \u001b[38;5;66;03m# _get_renderer may change the figure dpi (as vector formats\u001b[39;00m\n\u001b[1;32m 2364\u001b[0m \u001b[38;5;66;03m# force the figure dpi to 72), so we need to set it again here.\u001b[39;00m\n\u001b[1;32m 2365\u001b[0m \u001b[38;5;28;01mwith\u001b[39;00m cbook\u001b[38;5;241m.\u001b[39m_setattr_cm(\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mfigure, dpi\u001b[38;5;241m=\u001b[39mdpi):\n\u001b[0;32m-> 2366\u001b[0m result \u001b[38;5;241m=\u001b[39m \u001b[43mprint_method\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 2367\u001b[0m \u001b[43m \u001b[49m\u001b[43mfilename\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 2368\u001b[0m \u001b[43m \u001b[49m\u001b[43mfacecolor\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mfacecolor\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 2369\u001b[0m \u001b[43m \u001b[49m\u001b[43medgecolor\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43medgecolor\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 2370\u001b[0m \u001b[43m \u001b[49m\u001b[43morientation\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43morientation\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 2371\u001b[0m \u001b[43m \u001b[49m\u001b[43mbbox_inches_restore\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43m_bbox_inches_restore\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 2372\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 2373\u001b[0m \u001b[38;5;28;01mfinally\u001b[39;00m:\n\u001b[1;32m 2374\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m bbox_inches \u001b[38;5;129;01mand\u001b[39;00m restore_bbox:\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/matplotlib/backend_bases.py:2232\u001b[0m, in \u001b[0;36mFigureCanvasBase._switch_canvas_and_return_print_method..\u001b[0;34m(*args, **kwargs)\u001b[0m\n\u001b[1;32m 2228\u001b[0m optional_kws \u001b[38;5;241m=\u001b[39m { \u001b[38;5;66;03m# Passed by print_figure for other renderers.\u001b[39;00m\n\u001b[1;32m 2229\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mdpi\u001b[39m\u001b[38;5;124m\"\u001b[39m, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mfacecolor\u001b[39m\u001b[38;5;124m\"\u001b[39m, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124medgecolor\u001b[39m\u001b[38;5;124m\"\u001b[39m, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124morientation\u001b[39m\u001b[38;5;124m\"\u001b[39m,\n\u001b[1;32m 2230\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mbbox_inches_restore\u001b[39m\u001b[38;5;124m\"\u001b[39m}\n\u001b[1;32m 2231\u001b[0m skip \u001b[38;5;241m=\u001b[39m optional_kws \u001b[38;5;241m-\u001b[39m {\u001b[38;5;241m*\u001b[39minspect\u001b[38;5;241m.\u001b[39msignature(meth)\u001b[38;5;241m.\u001b[39mparameters}\n\u001b[0;32m-> 2232\u001b[0m print_method \u001b[38;5;241m=\u001b[39m functools\u001b[38;5;241m.\u001b[39mwraps(meth)(\u001b[38;5;28;01mlambda\u001b[39;00m \u001b[38;5;241m*\u001b[39margs, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs: \u001b[43mmeth\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 2233\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43m{\u001b[49m\u001b[43mk\u001b[49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mv\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mfor\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mk\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mv\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;129;43;01min\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mkwargs\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mitems\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mif\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mk\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;129;43;01mnot\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[38;5;129;43;01min\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mskip\u001b[49m\u001b[43m}\u001b[49m\u001b[43m)\u001b[49m)\n\u001b[1;32m 2234\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m: \u001b[38;5;66;03m# Let third-parties do as they see fit.\u001b[39;00m\n\u001b[1;32m 2235\u001b[0m print_method \u001b[38;5;241m=\u001b[39m meth\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/matplotlib/backends/backend_agg.py:509\u001b[0m, in \u001b[0;36mFigureCanvasAgg.print_png\u001b[0;34m(self, filename_or_obj, metadata, pil_kwargs)\u001b[0m\n\u001b[1;32m 462\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mprint_png\u001b[39m(\u001b[38;5;28mself\u001b[39m, filename_or_obj, \u001b[38;5;241m*\u001b[39m, metadata\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mNone\u001b[39;00m, pil_kwargs\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mNone\u001b[39;00m):\n\u001b[1;32m 463\u001b[0m \u001b[38;5;250m \u001b[39m\u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[1;32m 464\u001b[0m \u001b[38;5;124;03m Write the figure to a PNG file.\u001b[39;00m\n\u001b[1;32m 465\u001b[0m \n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 507\u001b[0m \u001b[38;5;124;03m *metadata*, including the default 'Software' key.\u001b[39;00m\n\u001b[1;32m 508\u001b[0m \u001b[38;5;124;03m \"\"\"\u001b[39;00m\n\u001b[0;32m--> 509\u001b[0m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_print_pil\u001b[49m\u001b[43m(\u001b[49m\u001b[43mfilename_or_obj\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mpng\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mpil_kwargs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mmetadata\u001b[49m\u001b[43m)\u001b[49m\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/matplotlib/backends/backend_agg.py:457\u001b[0m, in \u001b[0;36mFigureCanvasAgg._print_pil\u001b[0;34m(self, filename_or_obj, fmt, pil_kwargs, metadata)\u001b[0m\n\u001b[1;32m 452\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21m_print_pil\u001b[39m(\u001b[38;5;28mself\u001b[39m, filename_or_obj, fmt, pil_kwargs, metadata\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mNone\u001b[39;00m):\n\u001b[1;32m 453\u001b[0m \u001b[38;5;250m \u001b[39m\u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[1;32m 454\u001b[0m \u001b[38;5;124;03m Draw the canvas, then save it using `.image.imsave` (to which\u001b[39;00m\n\u001b[1;32m 455\u001b[0m \u001b[38;5;124;03m *pil_kwargs* and *metadata* are forwarded).\u001b[39;00m\n\u001b[1;32m 456\u001b[0m \u001b[38;5;124;03m \"\"\"\u001b[39;00m\n\u001b[0;32m--> 457\u001b[0m \u001b[43mFigureCanvasAgg\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mdraw\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m)\u001b[49m\n\u001b[1;32m 458\u001b[0m mpl\u001b[38;5;241m.\u001b[39mimage\u001b[38;5;241m.\u001b[39mimsave(\n\u001b[1;32m 459\u001b[0m filename_or_obj, \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mbuffer_rgba(), \u001b[38;5;28mformat\u001b[39m\u001b[38;5;241m=\u001b[39mfmt, origin\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mupper\u001b[39m\u001b[38;5;124m\"\u001b[39m,\n\u001b[1;32m 460\u001b[0m dpi\u001b[38;5;241m=\u001b[39m\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mfigure\u001b[38;5;241m.\u001b[39mdpi, metadata\u001b[38;5;241m=\u001b[39mmetadata, pil_kwargs\u001b[38;5;241m=\u001b[39mpil_kwargs)\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/matplotlib/backends/backend_agg.py:400\u001b[0m, in \u001b[0;36mFigureCanvasAgg.draw\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 396\u001b[0m \u001b[38;5;66;03m# Acquire a lock on the shared font cache.\u001b[39;00m\n\u001b[1;32m 397\u001b[0m \u001b[38;5;28;01mwith\u001b[39;00m RendererAgg\u001b[38;5;241m.\u001b[39mlock, \\\n\u001b[1;32m 398\u001b[0m (\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mtoolbar\u001b[38;5;241m.\u001b[39m_wait_cursor_for_draw_cm() \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mtoolbar\n\u001b[1;32m 399\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m nullcontext()):\n\u001b[0;32m--> 400\u001b[0m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mfigure\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mdraw\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mrenderer\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 401\u001b[0m \u001b[38;5;66;03m# A GUI class may be need to update a window using this draw, so\u001b[39;00m\n\u001b[1;32m 402\u001b[0m \u001b[38;5;66;03m# don't forget to call the superclass.\u001b[39;00m\n\u001b[1;32m 403\u001b[0m \u001b[38;5;28msuper\u001b[39m()\u001b[38;5;241m.\u001b[39mdraw()\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/matplotlib/artist.py:95\u001b[0m, in \u001b[0;36m_finalize_rasterization..draw_wrapper\u001b[0;34m(artist, renderer, *args, **kwargs)\u001b[0m\n\u001b[1;32m 93\u001b[0m \u001b[38;5;129m@wraps\u001b[39m(draw)\n\u001b[1;32m 94\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mdraw_wrapper\u001b[39m(artist, renderer, \u001b[38;5;241m*\u001b[39margs, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs):\n\u001b[0;32m---> 95\u001b[0m result \u001b[38;5;241m=\u001b[39m \u001b[43mdraw\u001b[49m\u001b[43m(\u001b[49m\u001b[43martist\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mrenderer\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 96\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m renderer\u001b[38;5;241m.\u001b[39m_rasterizing:\n\u001b[1;32m 97\u001b[0m renderer\u001b[38;5;241m.\u001b[39mstop_rasterizing()\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/matplotlib/artist.py:72\u001b[0m, in \u001b[0;36mallow_rasterization..draw_wrapper\u001b[0;34m(artist, renderer)\u001b[0m\n\u001b[1;32m 69\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m artist\u001b[38;5;241m.\u001b[39mget_agg_filter() \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[1;32m 70\u001b[0m renderer\u001b[38;5;241m.\u001b[39mstart_filter()\n\u001b[0;32m---> 72\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mdraw\u001b[49m\u001b[43m(\u001b[49m\u001b[43martist\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mrenderer\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 73\u001b[0m \u001b[38;5;28;01mfinally\u001b[39;00m:\n\u001b[1;32m 74\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m artist\u001b[38;5;241m.\u001b[39mget_agg_filter() \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/matplotlib/figure.py:3140\u001b[0m, in \u001b[0;36mFigure.draw\u001b[0;34m(self, renderer)\u001b[0m\n\u001b[1;32m 3137\u001b[0m \u001b[38;5;66;03m# ValueError can occur when resizing a window.\u001b[39;00m\n\u001b[1;32m 3139\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mpatch\u001b[38;5;241m.\u001b[39mdraw(renderer)\n\u001b[0;32m-> 3140\u001b[0m \u001b[43mmimage\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_draw_list_compositing_images\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 3141\u001b[0m \u001b[43m \u001b[49m\u001b[43mrenderer\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43martists\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43msuppressComposite\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 3143\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m sfig \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39msubfigs:\n\u001b[1;32m 3144\u001b[0m sfig\u001b[38;5;241m.\u001b[39mdraw(renderer)\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/matplotlib/image.py:131\u001b[0m, in \u001b[0;36m_draw_list_compositing_images\u001b[0;34m(renderer, parent, artists, suppress_composite)\u001b[0m\n\u001b[1;32m 129\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m not_composite \u001b[38;5;129;01mor\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m has_images:\n\u001b[1;32m 130\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m a \u001b[38;5;129;01min\u001b[39;00m artists:\n\u001b[0;32m--> 131\u001b[0m \u001b[43ma\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mdraw\u001b[49m\u001b[43m(\u001b[49m\u001b[43mrenderer\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 132\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m 133\u001b[0m \u001b[38;5;66;03m# Composite any adjacent images together\u001b[39;00m\n\u001b[1;32m 134\u001b[0m image_group \u001b[38;5;241m=\u001b[39m []\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/matplotlib/artist.py:72\u001b[0m, in \u001b[0;36mallow_rasterization..draw_wrapper\u001b[0;34m(artist, renderer)\u001b[0m\n\u001b[1;32m 69\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m artist\u001b[38;5;241m.\u001b[39mget_agg_filter() \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[1;32m 70\u001b[0m renderer\u001b[38;5;241m.\u001b[39mstart_filter()\n\u001b[0;32m---> 72\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mdraw\u001b[49m\u001b[43m(\u001b[49m\u001b[43martist\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mrenderer\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 73\u001b[0m \u001b[38;5;28;01mfinally\u001b[39;00m:\n\u001b[1;32m 74\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m artist\u001b[38;5;241m.\u001b[39mget_agg_filter() \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/matplotlib/axes/_base.py:3064\u001b[0m, in \u001b[0;36m_AxesBase.draw\u001b[0;34m(self, renderer)\u001b[0m\n\u001b[1;32m 3061\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m artists_rasterized:\n\u001b[1;32m 3062\u001b[0m _draw_rasterized(\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mfigure, artists_rasterized, renderer)\n\u001b[0;32m-> 3064\u001b[0m \u001b[43mmimage\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_draw_list_compositing_images\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 3065\u001b[0m \u001b[43m \u001b[49m\u001b[43mrenderer\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43martists\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mfigure\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43msuppressComposite\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 3067\u001b[0m renderer\u001b[38;5;241m.\u001b[39mclose_group(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124maxes\u001b[39m\u001b[38;5;124m'\u001b[39m)\n\u001b[1;32m 3068\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mstale \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mFalse\u001b[39;00m\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/matplotlib/image.py:131\u001b[0m, in \u001b[0;36m_draw_list_compositing_images\u001b[0;34m(renderer, parent, artists, suppress_composite)\u001b[0m\n\u001b[1;32m 129\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m not_composite \u001b[38;5;129;01mor\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m has_images:\n\u001b[1;32m 130\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m a \u001b[38;5;129;01min\u001b[39;00m artists:\n\u001b[0;32m--> 131\u001b[0m \u001b[43ma\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mdraw\u001b[49m\u001b[43m(\u001b[49m\u001b[43mrenderer\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 132\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m 133\u001b[0m \u001b[38;5;66;03m# Composite any adjacent images together\u001b[39;00m\n\u001b[1;32m 134\u001b[0m image_group \u001b[38;5;241m=\u001b[39m []\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/matplotlib/artist.py:72\u001b[0m, in \u001b[0;36mallow_rasterization..draw_wrapper\u001b[0;34m(artist, renderer)\u001b[0m\n\u001b[1;32m 69\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m artist\u001b[38;5;241m.\u001b[39mget_agg_filter() \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[1;32m 70\u001b[0m renderer\u001b[38;5;241m.\u001b[39mstart_filter()\n\u001b[0;32m---> 72\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mdraw\u001b[49m\u001b[43m(\u001b[49m\u001b[43martist\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mrenderer\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 73\u001b[0m \u001b[38;5;28;01mfinally\u001b[39;00m:\n\u001b[1;32m 74\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m artist\u001b[38;5;241m.\u001b[39mget_agg_filter() \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/matplotlib/axis.py:1376\u001b[0m, in \u001b[0;36mAxis.draw\u001b[0;34m(self, renderer, *args, **kwargs)\u001b[0m\n\u001b[1;32m 1373\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m\n\u001b[1;32m 1374\u001b[0m renderer\u001b[38;5;241m.\u001b[39mopen_group(\u001b[38;5;18m__name__\u001b[39m, gid\u001b[38;5;241m=\u001b[39m\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mget_gid())\n\u001b[0;32m-> 1376\u001b[0m ticks_to_draw \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_update_ticks\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 1377\u001b[0m tlb1, tlb2 \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_get_ticklabel_bboxes(ticks_to_draw, renderer)\n\u001b[1;32m 1379\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m tick \u001b[38;5;129;01min\u001b[39;00m ticks_to_draw:\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/matplotlib/axis.py:1263\u001b[0m, in \u001b[0;36mAxis._update_ticks\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 1258\u001b[0m \u001b[38;5;250m\u001b[39m\u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[1;32m 1259\u001b[0m \u001b[38;5;124;03mUpdate ticks (position and labels) using the current data interval of\u001b[39;00m\n\u001b[1;32m 1260\u001b[0m \u001b[38;5;124;03mthe axes. Return the list of ticks that will be drawn.\u001b[39;00m\n\u001b[1;32m 1261\u001b[0m \u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[1;32m 1262\u001b[0m major_locs \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mget_majorticklocs()\n\u001b[0;32m-> 1263\u001b[0m major_labels \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mmajor\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mformatter\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mformat_ticks\u001b[49m\u001b[43m(\u001b[49m\u001b[43mmajor_locs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 1264\u001b[0m major_ticks \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mget_major_ticks(\u001b[38;5;28mlen\u001b[39m(major_locs))\n\u001b[1;32m 1265\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mmajor\u001b[38;5;241m.\u001b[39mformatter\u001b[38;5;241m.\u001b[39mset_locs(major_locs)\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/matplotlib/ticker.py:218\u001b[0m, in \u001b[0;36mFormatter.format_ticks\u001b[0;34m(self, values)\u001b[0m\n\u001b[1;32m 216\u001b[0m \u001b[38;5;250m\u001b[39m\u001b[38;5;124;03m\"\"\"Return the tick labels for all the ticks at once.\"\"\"\u001b[39;00m\n\u001b[1;32m 217\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mset_locs(values)\n\u001b[0;32m--> 218\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m [\u001b[38;5;28mself\u001b[39m(value, i) \u001b[38;5;28;01mfor\u001b[39;00m i, value \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28menumerate\u001b[39m(values)]\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/matplotlib/ticker.py:218\u001b[0m, in \u001b[0;36m\u001b[0;34m(.0)\u001b[0m\n\u001b[1;32m 216\u001b[0m \u001b[38;5;250m\u001b[39m\u001b[38;5;124;03m\"\"\"Return the tick labels for all the ticks at once.\"\"\"\u001b[39;00m\n\u001b[1;32m 217\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mset_locs(values)\n\u001b[0;32m--> 218\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m [\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mvalue\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mi\u001b[49m\u001b[43m)\u001b[49m \u001b[38;5;28;01mfor\u001b[39;00m i, value \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28menumerate\u001b[39m(values)]\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/matplotlib/dates.py:651\u001b[0m, in \u001b[0;36mDateFormatter.__call__\u001b[0;34m(self, x, pos)\u001b[0m\n\u001b[1;32m 650\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21m__call__\u001b[39m(\u001b[38;5;28mself\u001b[39m, x, pos\u001b[38;5;241m=\u001b[39m\u001b[38;5;241m0\u001b[39m):\n\u001b[0;32m--> 651\u001b[0m result \u001b[38;5;241m=\u001b[39m \u001b[43mnum2date\u001b[49m\u001b[43m(\u001b[49m\u001b[43mx\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mtz\u001b[49m\u001b[43m)\u001b[49m\u001b[38;5;241m.\u001b[39mstrftime(\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mfmt)\n\u001b[1;32m 652\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m _wrap_in_tex(result) \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_usetex \u001b[38;5;28;01melse\u001b[39;00m result\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/matplotlib/dates.py:544\u001b[0m, in \u001b[0;36mnum2date\u001b[0;34m(x, tz)\u001b[0m\n\u001b[1;32m 518\u001b[0m \u001b[38;5;250m\u001b[39m\u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[1;32m 519\u001b[0m \u001b[38;5;124;03mConvert Matplotlib dates to `~datetime.datetime` objects.\u001b[39;00m\n\u001b[1;32m 520\u001b[0m \n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 541\u001b[0m \u001b[38;5;124;03mFor details, see the module docstring.\u001b[39;00m\n\u001b[1;32m 542\u001b[0m \u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[1;32m 543\u001b[0m tz \u001b[38;5;241m=\u001b[39m _get_tzinfo(tz)\n\u001b[0;32m--> 544\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43m_from_ordinalf_np_vectorized\u001b[49m\u001b[43m(\u001b[49m\u001b[43mx\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mtz\u001b[49m\u001b[43m)\u001b[49m\u001b[38;5;241m.\u001b[39mtolist()\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/numpy/lib/function_base.py:2329\u001b[0m, in \u001b[0;36mvectorize.__call__\u001b[0;34m(self, *args, **kwargs)\u001b[0m\n\u001b[1;32m 2326\u001b[0m vargs \u001b[38;5;241m=\u001b[39m [args[_i] \u001b[38;5;28;01mfor\u001b[39;00m _i \u001b[38;5;129;01min\u001b[39;00m inds]\n\u001b[1;32m 2327\u001b[0m vargs\u001b[38;5;241m.\u001b[39mextend([kwargs[_n] \u001b[38;5;28;01mfor\u001b[39;00m _n \u001b[38;5;129;01min\u001b[39;00m names])\n\u001b[0;32m-> 2329\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_vectorize_call\u001b[49m\u001b[43m(\u001b[49m\u001b[43mfunc\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mfunc\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43margs\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mvargs\u001b[49m\u001b[43m)\u001b[49m\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/numpy/lib/function_base.py:2412\u001b[0m, in \u001b[0;36mvectorize._vectorize_call\u001b[0;34m(self, func, args)\u001b[0m\n\u001b[1;32m 2409\u001b[0m \u001b[38;5;66;03m# Convert args to object arrays first\u001b[39;00m\n\u001b[1;32m 2410\u001b[0m inputs \u001b[38;5;241m=\u001b[39m [asanyarray(a, dtype\u001b[38;5;241m=\u001b[39m\u001b[38;5;28mobject\u001b[39m) \u001b[38;5;28;01mfor\u001b[39;00m a \u001b[38;5;129;01min\u001b[39;00m args]\n\u001b[0;32m-> 2412\u001b[0m outputs \u001b[38;5;241m=\u001b[39m \u001b[43mufunc\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43minputs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 2414\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m ufunc\u001b[38;5;241m.\u001b[39mnout \u001b[38;5;241m==\u001b[39m \u001b[38;5;241m1\u001b[39m:\n\u001b[1;32m 2415\u001b[0m res \u001b[38;5;241m=\u001b[39m asanyarray(outputs, dtype\u001b[38;5;241m=\u001b[39motypes[\u001b[38;5;241m0\u001b[39m])\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/matplotlib/dates.py:359\u001b[0m, in \u001b[0;36m_from_ordinalf\u001b[0;34m(x, tz)\u001b[0m\n\u001b[1;32m 346\u001b[0m \u001b[38;5;250m\u001b[39m\u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[1;32m 347\u001b[0m \u001b[38;5;124;03mConvert Gregorian float of the date, preserving hours, minutes,\u001b[39;00m\n\u001b[1;32m 348\u001b[0m \u001b[38;5;124;03mseconds and microseconds. Return value is a `.datetime`.\u001b[39;00m\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 353\u001b[0m \u001b[38;5;124;03m:rc:`timezone`.\u001b[39;00m\n\u001b[1;32m 354\u001b[0m \u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[1;32m 356\u001b[0m tz \u001b[38;5;241m=\u001b[39m _get_tzinfo(tz)\n\u001b[1;32m 358\u001b[0m dt \u001b[38;5;241m=\u001b[39m (np\u001b[38;5;241m.\u001b[39mdatetime64(get_epoch()) \u001b[38;5;241m+\u001b[39m\n\u001b[0;32m--> 359\u001b[0m \u001b[43mnp\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mtimedelta64\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mint\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mnp\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mround\u001b[49m\u001b[43m(\u001b[49m\u001b[43mx\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43m \u001b[49m\u001b[43mMUSECONDS_PER_DAY\u001b[49m\u001b[43m)\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43mus\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m)\u001b[49m)\n\u001b[1;32m 360\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m dt \u001b[38;5;241m<\u001b[39m np\u001b[38;5;241m.\u001b[39mdatetime64(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m0001-01-01\u001b[39m\u001b[38;5;124m'\u001b[39m) \u001b[38;5;129;01mor\u001b[39;00m dt \u001b[38;5;241m>\u001b[39m\u001b[38;5;241m=\u001b[39m np\u001b[38;5;241m.\u001b[39mdatetime64(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m10000-01-01\u001b[39m\u001b[38;5;124m'\u001b[39m):\n\u001b[1;32m 361\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mDate ordinal \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mx\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m converts to \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mdt\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m (using \u001b[39m\u001b[38;5;124m'\u001b[39m\n\u001b[1;32m 362\u001b[0m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mepoch \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mget_epoch()\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m), but Matplotlib dates must be \u001b[39m\u001b[38;5;124m'\u001b[39m\n\u001b[1;32m 363\u001b[0m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mbetween year 0001 and 9999.\u001b[39m\u001b[38;5;124m'\u001b[39m)\n", + "\u001b[0;31mOverflowError\u001b[0m: int too big to convert" + ] + }, + { + "data": { + "text/plain": [ + "Canvas(toolbar=Toolbar(toolitems=[('Home', 'Reset original view', 'home', 'home'), ('Back', 'Back to previous view', 'arrow-left', 'back'), ('Forward', 'Forward to next view', 'arrow-right', 'forward'), ('Pan', 'Left button pans, Right button zooms\\nx/y fixes axis, CTRL fixes aspect', 'arrows', 'pan'), ('Zoom', 'Zoom to rectangle\\nx/y fixes axis', 'square-o', 'zoom'), ('Download', 'Download plot', 'floppy-o', 'save_figure')]))" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'gswfluxdn'" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [ + { + "ename": "KeyError", + "evalue": "'gswfluxdn'", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mKeyError\u001b[0m Traceback (most recent call last)", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/core/dataset.py:1348\u001b[0m, in \u001b[0;36mDataset._construct_dataarray\u001b[0;34m(self, name)\u001b[0m\n\u001b[1;32m 1347\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m-> 1348\u001b[0m variable \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_variables\u001b[49m\u001b[43m[\u001b[49m\u001b[43mname\u001b[49m\u001b[43m]\u001b[49m\n\u001b[1;32m 1349\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mKeyError\u001b[39;00m:\n", + "\u001b[0;31mKeyError\u001b[0m: 'gswfluxdn'", + "\nDuring handling of the above exception, another exception occurred:\n", + "\u001b[0;31mKeyError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[0;32mIn[12], line 7\u001b[0m\n\u001b[1;32m 5\u001b[0m qc_display \u001b[38;5;241m=\u001b[39m act\u001b[38;5;241m.\u001b[39mplotting\u001b[38;5;241m.\u001b[39mTimeSeriesDisplay(ds)\n\u001b[1;32m 6\u001b[0m qc_display\u001b[38;5;241m.\u001b[39madd_subplots((\u001b[38;5;241m2\u001b[39m,), figsize \u001b[38;5;241m=\u001b[39m (\u001b[38;5;241m9.5\u001b[39m,\u001b[38;5;241m10\u001b[39m))\n\u001b[0;32m----> 7\u001b[0m qc_ax \u001b[38;5;241m=\u001b[39m \u001b[43mqc_display\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mplot\u001b[49m\u001b[43m(\u001b[49m\u001b[43mqc_variable\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43msubplot_index\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m0\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mset_title\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mQC results on field: \u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m \u001b[49m\u001b[38;5;241;43m+\u001b[39;49m\u001b[43m \u001b[49m\u001b[43mqc_variable\u001b[49m\u001b[43m,\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 8\u001b[0m qc_ax\u001b[38;5;241m.\u001b[39mgrid()\n\u001b[1;32m 9\u001b[0m qc_display\u001b[38;5;241m.\u001b[39mqc_flag_block_plot(qc_variable, subplot_index\u001b[38;5;241m=\u001b[39m(\u001b[38;5;241m1\u001b[39m,))\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/act/plotting/timeseriesdisplay.py:418\u001b[0m, in \u001b[0;36mTimeSeriesDisplay.plot\u001b[0;34m(self, field, dsname, subplot_index, cmap, set_title, add_nan, day_night_background, invert_y_axis, abs_limits, time_rng, y_rng, use_var_for_y, set_shading, assessment_overplot, overplot_marker, overplot_behind, overplot_markersize, assessment_overplot_category, assessment_overplot_category_color, force_line_plot, labels, cbar_label, cbar_h_adjust, secondary_y, y_axis_flag_meanings, colorbar_labels, cb_friendly, **kwargs)\u001b[0m\n\u001b[1;32m 415\u001b[0m assessment_overplot_category_color[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mAcceptable\u001b[39m\u001b[38;5;124m'\u001b[39m] \u001b[38;5;241m=\u001b[39m (\u001b[38;5;241m0.0\u001b[39m, \u001b[38;5;241m0.4240129715562796\u001b[39m, \u001b[38;5;241m0.4240129715562796\u001b[39m),\n\u001b[1;32m 417\u001b[0m \u001b[38;5;66;03m# Get data and dimensions\u001b[39;00m\n\u001b[0;32m--> 418\u001b[0m data \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_obj\u001b[49m\u001b[43m[\u001b[49m\u001b[43mdsname\u001b[49m\u001b[43m]\u001b[49m\u001b[43m[\u001b[49m\u001b[43mfield\u001b[49m\u001b[43m]\u001b[49m\n\u001b[1;32m 419\u001b[0m dim \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mlist\u001b[39m(\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_obj[dsname][field]\u001b[38;5;241m.\u001b[39mdims)\n\u001b[1;32m 420\u001b[0m xdata \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_obj[dsname][dim[\u001b[38;5;241m0\u001b[39m]]\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/core/dataset.py:1439\u001b[0m, in \u001b[0;36mDataset.__getitem__\u001b[0;34m(self, key)\u001b[0m\n\u001b[1;32m 1437\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39misel(\u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkey)\n\u001b[1;32m 1438\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m utils\u001b[38;5;241m.\u001b[39mhashable(key):\n\u001b[0;32m-> 1439\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_construct_dataarray\u001b[49m\u001b[43m(\u001b[49m\u001b[43mkey\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 1440\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m utils\u001b[38;5;241m.\u001b[39miterable_of_hashable(key):\n\u001b[1;32m 1441\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_copy_listed(key)\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/core/dataset.py:1350\u001b[0m, in \u001b[0;36mDataset._construct_dataarray\u001b[0;34m(self, name)\u001b[0m\n\u001b[1;32m 1348\u001b[0m variable \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_variables[name]\n\u001b[1;32m 1349\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mKeyError\u001b[39;00m:\n\u001b[0;32m-> 1350\u001b[0m _, name, variable \u001b[38;5;241m=\u001b[39m \u001b[43m_get_virtual_variable\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_variables\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mname\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mdims\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 1352\u001b[0m needed_dims \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mset\u001b[39m(variable\u001b[38;5;241m.\u001b[39mdims)\n\u001b[1;32m 1354\u001b[0m coords: \u001b[38;5;28mdict\u001b[39m[Hashable, Variable] \u001b[38;5;241m=\u001b[39m {}\n", + "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/core/dataset.py:186\u001b[0m, in \u001b[0;36m_get_virtual_variable\u001b[0;34m(variables, key, dim_sizes)\u001b[0m\n\u001b[1;32m 184\u001b[0m split_key \u001b[38;5;241m=\u001b[39m key\u001b[38;5;241m.\u001b[39msplit(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m.\u001b[39m\u001b[38;5;124m\"\u001b[39m, \u001b[38;5;241m1\u001b[39m)\n\u001b[1;32m 185\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mlen\u001b[39m(split_key) \u001b[38;5;241m!=\u001b[39m \u001b[38;5;241m2\u001b[39m:\n\u001b[0;32m--> 186\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mKeyError\u001b[39;00m(key)\n\u001b[1;32m 188\u001b[0m ref_name, var_name \u001b[38;5;241m=\u001b[39m split_key\n\u001b[1;32m 189\u001b[0m ref_var \u001b[38;5;241m=\u001b[39m variables[ref_name]\n", + "\u001b[0;31mKeyError\u001b[0m: 'gswfluxdn'" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "fd79e0ba342048999f2bf386b4218b60", + "version_major": 2, + "version_minor": 0 + }, + "image/png": "iVBORw0KGgoAAAANSUhEUgAAA7YAAAPoCAYAAADqfmIfAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAA9hAAAPYQGoP6dpAABCA0lEQVR4nO3df2zV9b348Veh0Kr3toswKwgy3NXJLhm7lMAot1l0WgOGG5LdwOKNqBeTNdsuAa7egdzoICbN3c3MvU7BLYJmCbrGn/GPXkdzcy8/hJuMpiyLkLtFuBa2VlLMWtTdIvD5/mHo93YtDpCe9oWPR3L+OO99PvR1trfdefL5HE9ZURRFAAAAQFJjRnoAAAAA+CSELQAAAKkJWwAAAFITtgAAAKQmbAEAAEhN2AIAAJCasAUAACA1YQsAAEBqwhYAAIDUhC0AAACpCVsAAABSE7YAAACkJmwBAABITdgCAACQmrAFAAAgNWELAABAasIWAACA1IQtAAAAqQlbAAAAUhO2AAAApCZsAQAASE3YAgAAkJqwBQAAIDVhCwAAQGrCFgAAgNSELQAAAKkJWwAAAFITtgAAAKQmbAEAAEhN2AIAAJCasAUAACA1YQsAAEBqwhYAAIDUhC0AAACpCVsAAABSE7YAAACkJmwBAABITdgCAACQmrAFAAAgNWELAABAasIWAACA1IQtAAAAqQlbAAAAUhO2AAAApCZsAQAASE3YAgAAkJqwBQAAIDVhCwAAQGrCFgAAgNSELQAAAKkJWwAAAFITtgAAAKQmbAEAAEhN2AIAAJCasAUAACA1YQsAAEBqwhYAAIDUhC0AAACpCVsAAABSE7YAAACkJmwBAABITdgCAACQmrAFAAAgNWELAABAasIWAACA1IQtAAAAqQlbAAAAUhO2AAAApCZsAQAASE3YAgAAkJqwBQAAIDVhCwAAQGrCFgAAgNSELQAAAKkJWwAAAFITtgAAAKQmbAEAAEhN2AIAAJCasAUAACA1YQsAAEBqwhYAAIDUhC0AAACpCVsAAABSE7YAAACkJmwBAABITdgCAACQmrAFAAAgNWELAABAasIWAACA1IQtAAAAqQlbAAAAUhO2AAAApCZsAQAASE3YAgAAkJqwBQAAIDVhCwAAQGrCFgAAgNSELQAAAKkJWwAAAFITtgAAAKQmbAEAAEhN2AIAAJCasAUAACA1YQsAAEBqwhYAAIDUhC0AAACpCVsAAABSE7YAAACkJmwBAABITdgCAACQmrAFAAAgNWELAABAasIWAACA1IQtAAAAqQlbAAAAUhO2w2znzp2xePHimDx5cpSVlcWrr776R8/ZsWNH1NbWRmVlZdxwww3x1FNPDf+gAAAASQnbYfb+++/HrFmz4oknnjiv4w8fPhyLFi2K+vr6aG9vj4ceeihWrlwZL7300jBPCgAAkFNZURTFSA/xaVFWVhavvPJKLFmy5JzHfPe7343XXnstDh482L/W2NgYv/jFL2Lv3r0lmBIAACCX8pEegIH27t0bDQ0NA9buuOOO2LJlS3z44Ycxbty4Ic/r6+uLvr6+/udnzpyJd999NyZMmBBlZWXDOjMAAHzaFUURJ06ciMmTJ8eYMW6MLTVhO8p0dXVFTU3NgLWampo4depUdHd3x6RJk4Y8r6mpKTZs2FCKEQEAgHM4cuRITJkyZaTH+NQRtqPQH15hPXu3+MddeV23bl2sWbOm/3lPT09cf/31ceTIkaiqqhqeQQEAgIiI6O3tjalTp8af/umfjvQon0rCdpS59tpro6ura8DasWPHory8PCZMmHDO8yoqKqKiomLQelVVlbAFAIAS8THAkeHm71Fm/vz50draOmBt+/btMWfOnHN+vhYAAODTTNgOs/feey/2798f+/fvj4iPvs5n//790dHREREf3UK8fPny/uMbGxvj7bffjjVr1sTBgwdj69atsWXLlnjggQdGYnwAAIBRz63Iw2zfvn1xyy239D8/+znYe+65J5599tno7Ozsj9yIiOnTp0dLS0usXr06nnzyyZg8eXI8/vjj8fWvf73kswMAAGTge2wvU729vVFdXR09PT0+YwsAAMPM+++R5VZkAAAAUhO2AAAApCZsAQAASE3YAgAAkJqwBQAAIDVhCwAAQGrCFgAAgNSELQAAAKkJWwAAAFITtgAAAKQmbAEAAEhN2AIAAJCasAUAACA1YQsAAEBqwhYAAIDUhC0AAACpCVsAAABSE7YAAACkJmwBAABITdgCAACQmrAFAAAgNWELAABAasIWAACA1IQtAAAAqQlbAAAAUhO2AAAApCZsAQAASE3YAgAAkJqwBQAAIDVhCwAAQGrCFgAAgNSELQAAAKkJWwAAAFITtgAAAKQmbAEAAEhN2AIAAJCasAUAACA1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338/Zs2aFU888cR5He/9d4kVpDR37tyisbFxwNrNN99crF27dsjj/+Ef/qG4+eabB6x985vfLL7yla8M24zkcKF7aShf/OIXiw0bNlzq0UjmYvfSsmXLin/8x38sHnnkkWLWrFnDOCFZXOhe+rd/+7eiurq6OH78eCnGI5kL3U///M//XNxwww0D1h5//PFiypQpwzYj+URE8corr3zsMd5/l5YrtgmdPHky2traoqGhYcB6Q0ND7NmzZ8hz9u7dO+j4O+64I/bt2xcffvjhsM3K6HYxe+kPnTlzJk6cOBFXX331cIxIEhe7l5555pl466234pFHHhnuEUniYvbSa6+9FnPmzInvf//7cd1118VNN90UDzzwQPz+978vxciMYhezn+rq6uLo0aPR0tISRVHEO++8Ey+++GLceeedpRiZy4j336VVPtIDcOG6u7vj9OnTUVNTM2C9pqYmurq6hjynq6tryONPnToV3d3dMWnSpGGbl9HrYvbSH/rBD34Q77//fixdunQ4RiSJi9lLv/71r2Pt2rWxa9euKC/3f0d85GL20qFDh2L37t1RWVkZr7zySnR3d8e3vvWtePfdd33O9lPuYvZTXV1dbNu2LZYtWxb/+7//G6dOnYq/+qu/ih/+8IelGJnLiPffpeWKbWJlZWUDnhdFMWjtjx0/1DqfPhe6l856/vnn43vf+140NzfHNddcM1zjkcj57qXTp0/HXXfdFRs2bIibbrqpVOORyIX8Xjpz5kyUlZXFtm3bYu7cubFo0aJ47LHH4tlnn3XVloi4sP104MCBWLlyZTz88MPR1tYWr7/+ehw+fDgaGxtLMSqXGe+/S8dfkSc0ceLEGDt27KC/aTx27NigvxU669prrx3y+PLy8pgwYcKwzcrodjF76azm5uZYsWJFvPDCC3HbbbcN55gkcKF76cSJE7Fv375ob2+P73znOxHxUZwURRHl5eWxffv2uPXWW0syO6PLxfxemjRpUlx33XVRXV3dvzZjxowoiiKOHj0aN95447DOzOh1MfupqakpFixYEA8++GBERHzpS1+Kq666Kurr6+PRRx91lY3z5v13ablim9D48eOjtrY2WltbB6y3trZGXV3dkOfMnz9/0PHbt2+POXPmxLhx44ZtVka3i9lLER9dqb333nvjueee85kjIuLC91JVVVX88pe/jP379/c/Ghsb4wtf+ELs378/5s2bV6rRGWUu5vfSggUL4re//W289957/Wu/+tWvYsyYMTFlypRhnZfR7WL20wcffBBjxgx8izx27NiI+P9X2+B8eP9dYiP0L63iE/rpT39ajBs3rtiyZUtx4MCBYtWqVcVVV11V/M///E9RFEWxdu3a4u677+4//tChQ8WVV15ZrF69ujhw4ECxZcuWYty4ccWLL744Ui+BUeJC99Jzzz1XlJeXF08++WTR2dnZ//jd7343Ui+BUeJC99If8m9F5qwL3UsnTpwopkyZUvz1X/918eabbxY7duwobrzxxuL+++8fqZfAKHKh++mZZ54pysvLi02bNhVvvfVWsXv37mLOnDnF3LlzR+olMEqcOHGiaG9vL9rb24uIKB577LGivb29ePvtt4ui8P57pAnbxJ588sli2rRpxfjx44vZs2cXO3bs6P/P7rnnnuKrX/3qgOP/8z//s/iLv/iLYvz48cXnPve5YvPmzSWemNHqQvbSV7/61SIiBj3uueee0g/OqHOhv5f+L2HL/3Whe+ngwYPFbbfdVlxxxRXFlClTijVr1hQffPBBiadmtLrQ/fT4448XX/ziF4srrriimDRpUvE3f/M3xdGjR0s8NaPNf/zHf3zseyDvv0dWWVG4pwIAAIC8fMYWAACA1IQtAAAAqQlbAAAAUhO2AAAApCZsAQAASE3YAgAAkJqwBQAAIDVhCwAAQGrCFgAAgNSELQAAAKkJWwAAAFITtgAAAKQmbAEAAEhN2AIAAJCasAUAACA1YQsAAEBqwhYAAIDUhC0AAACpCVsAAABSE7YAAACkJmwBAABITdgCAACQmrAFAAAgNWELAABAasIWAACA1IQtAAAAqQlbAAAAUhO2AAAApCZsAQAASE3YAgAAkJqwBQAAIDVhCwAAQGrCFgAAgNSELQAAAKkJWwAAAFITtgAAAKQmbAEAAEhN2AIAAJCasAUAACA1YQsAAEBqwhYAAIDUhC0AAACpCVsAAABSE7YAAACkJmyH2c6dO2Px4sUxefLkKCsri1dfffWPnrNjx46ora2NysrKuOGGG+Kpp54a/kEBAACSErbD7P33349Zs2bFE088cV7HHz58OBYtWhT19fXR3t4eDz30UKxcuTJeeumlYZ4UAAAgp7KiKIqRHuLToqysLF555ZVYsmTJOY/57ne/G6+99locPHiwf62xsTF+8YtfxN69e0swJQAAQC7lIz0AA+3duzcaGhoGrN1xxx2xZcuW+PDDD2PcuHFDntfX1xd9fX39z8+cORPvvvtuTJgwIcrKyoZ1ZgAA+LQriiJOnDgRkydPjjFj3BhbasJ2lOnq6oqampoBazU1NXHq1Kno7u6OSZMmDXleU1NTbNiwoRQjAgAA53DkyJGYMmXKSI/xqSNsR6E/vMJ69m7xj7vyum7dulizZk3/856enrj++uvjyJEjUVVVNTyDAgAAERHR29sbU6dOjT/90z8d6VE+lYTtKHPttddGV1fXgLVjx45FeXl5TJgw4ZznVVRUREVFxaD1qqoqYQsAACXiY4Ajw83fo8z8+fOjtbV1wNr27dtjzpw55/x8LQAAwKeZsB1m7733Xuzfvz/2798fER99nc/+/fujo6MjIj66hXj58uX9xzc2Nsbbb78da9asiYMHD8bWrVtjy5Yt8cADD4zE+AAAAKOeW5GH2b59++KWW27pf372c7D33HNPPPvss9HZ2dkfuRER06dPj5aWlli9enU8+eSTMXny5Hj88cfj61//eslnBwAAyMD32F6ment7o7q6Onp6enzGFgAAhpn33yPLrcgAAACkJmwBAABITdgCAACQmrAFAAAgNWELAABAasIWAACA1IQtAAAAqQlbAAAAUhO2AAAApCZsAQAASE3YAgAAkJqwBQAAIDVhCwAAQGrCFgAAgNSELQAAAKkJWwAAAFITtgAAAKQmbAEAAEhN2AIAAJCasAUAACA1YQsAAEBqwhYAAIDUhC0AAACpCVsAAABSE7YAAACkJmwBAABITdgCAACQmrAFAAAgNWELAABAasIWAACA1IQtAAAAqQlbAAAAUhO2AAAApCZsAQAASE3YAgAAkJqwBQAAIDVhCwAAQGrCFgAAgNSELQAAAKkJWwAAAFITtgAAAKQmbAEAAEhN2AIAAJCasAUAACA1YQsAAEBqwhYAAIDUhC0AAACpCVsAAABSE7YAAACkJmwBAABITdgCAACQmrAFAAAgNWELAABAasIWAACA1IQtAAAAqQlbAAAAUhO2AAAApCZsAQAASE3YAgAAkJqwLZFNmzbF9OnTo7KyMmpra2PXrl0fe/y2bdti1qxZceWVV8akSZPivvvui+PHj5doWgAAgDyEbQk0NzfHqlWrYv369dHe3h719fWx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TdgCAACQmrAFAAAgNWELAABAasIWAACA1IQtAAAAqQlbAAAAUhO2AAAApCZsAQAASE3YAgAAkJqwBQAAIDVhCwAAQGrCFgAAgNSELQAAAKkJWwAAAFITtgAAAKQmbAEAAEhN2AIAAJCasAUAACA1YQsAAEBqwhYAAIDUhC0AAACpCVsAAABSE7YAAACkJmwBAABITdgCAACQmrAFAAAgNWELAABAasIWAACA1IQtAAAAqQlbAAAAUhO2AAAApCZsAQAASE3YAgAAkJqwBQAAIDVhCwAAQGrCFgAAgNSELQAAAKkJWwAAAFITtgAAAKQmbAEAAEhN2AIAAJCasAUAACA1YQsAAEBqwhYAAIDUhC0AAACpCVsAAABSE7YAAACkJmwBAABITdgCAACQmrAFAAAgNWELAABAasIWAACA1IQtAAAAqQlbAAAAUhO2AAAApCZsS2TTpk0xffr0qKysjNra2ti1a9c5j3355Zfj9ttvj89+9rNRVVUV8+fPj5/97GclnBYAACAPYVsCzc3NsWrVqli/fn20t7dHfX19LFy4MDo6OoY8fufOnXH77bdHS0tLtLW1xS233BKLFy+O9vb2Ek8OAAAw+pUVRVGM9BCXu3nz5sXs2bNj8+bN/WszZsyIJUuWRFNT03n9GX/+538ey5Yti4cffvi8ju/t7Y3q6uro6emJqqqqi5obAAA4P95/jyxXbIfZyZMno62tLRoaGgasNzQ0xJ49e87rzzhz5kycOHEirr766nMe09fXF729vQMeAAAAnwbCdph1d3fH6dOno6amZsB6TU1NdHV1ndef8YMf/CDef//9WLp06TmPaWpqiurq6v7H1KlTP9HcAAAAWQjbEikrKxvwvCiKQWtDef755+N73/teNDc3xzXXXHPO49atWxc9PT39jyNHjnzimQEAADIoH+kBLncTJ06MsWPHDro6e+zYsUFXcf9Qc3NzrFixIl544YW47bbbPvbYioqKqKio+MTzAgAAZOOK7TAbP3581NbWRmtr64D11tbWqKurO+d5zz//fNx7773x3HPPxZ133jncYwIAAKTlim0JrFmzJu6+++6YM2dOzJ8/P3784x9HR0dHNDY2RsRHtxH/5je/iZ/85CcR8VHULl++PP71X/81vvKVr/Rf7b3iiiuiurp6xF4HAADAaCRsS2DZsmVx/Pjx2LhxY3R2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7WL20h86c+ZMnDhxIq6++urhGJEkLnYvPfPMM/HWW2/FI488MtwjksTF7KXXXnst5syZE9///vfjuuuui5tuuikeeOCB+P3vf1+KkRnFLmY/1dXVxdGjR6OlpSWKooh33nknXnzxxbjzzjtLMTKXEe+/S6t8pAfgwnV3d8fp06ejpqZmwHpNTU10dXUNeU5XV9eQx586dSq6u7tj0qRJwzYvo9fF7KU/9IMf/CDef//9WLp06XCMSBIXs5d+/etfx9q1a2PXrl1RXu7/jvjIxeylQ4cOxe7du6OysjJeeeWV6O7ujm9961vx7rvv+pztp9zF7Ke6urrYtm1bLFu2LP73f/83Tp06FX/1V38VP/zhD0sxMpcR779LyxXbxMrKygY8L4pi0NofO36odT59LnQvnfX888/H9773vWhubo5rrrlmuMYjkfPdS6dPn4677rorNmzYEDfddFOpxiORC/m9dObMmSgrK4tt27bF3LlzY9GiRfHYY4/Fs88+66otEXFh++nAgQOxcuXKePjhh6OtrS1ef/31OHz4cDQ2NpZiVC4z3n+Xjr8iT2jixIkxduzYQX/TeOzYsUF/K3TWtddeO+Tx5eXlMWHChGGbldHtYvbSWc3NzbFixYp44YUX4rbbbhvOMUngQvfSiRMnYt++fdHe3h7f+c53IuKjOCmKIsrLy2P79u1x6623lmR2RpeL+b00adKkuO6666K6urp/bcaMGVEURRw9ejRuvPHGYZ2Z0eti9lNTU1MsWLAgHnzwwYiI+NKXvhRXXXVV1NfXx6OPPuoqG+fN++/ScsU2ofHjx0dtbW20trYOWG9tbY26urohz5k/f/6g47dv3x5z5syJcePGDdusjG4Xs5ciPrpSe++998Zzzz3nM0dExIXvpaqqqvjlL38Z+/fv7380NjbGF77whdi/f3/MmzevVKMzylzM76UFCxbEb3/723jvvff61371q1/FmDFjYsqUKcM6L6PbxeynDz74IMaMGfgWeezYsRHx/6+2wfnw/rvERuhfWsUn9NOf/rQYN25csWXLluLAgQPFqlWriquuuqr4n//5n6IoimLt2rXF3Xff3X/8oUOHiiuvvLJYvXp1ceDAgWLLli3FuHHjihdffHGkXgKjxIXupeeee64oLy8vnnzyyaKzs7P/8bvf/W6kXgKjxIXupT/k34rMWRe6l06cOFFMmTKl+Ou//uvizTffLHbs2FHceOONxf333z9SL4FR5EL30zPPPFOUl5cXmzZtKt56661i9+7dxZw5c4q5c+eO1EtglDhx4kTR3t5etLe3FxFRPPbYY0V7e3vx9ttvF0Xh/fdIE7aJPfnkk8W0adOK8ePHF7Nnzy527NjR/5/dc889xVe/+tUBx//nf/5n8Rd/8RfF+PHji8997nPF5s2bSzwxo9WF7KWvfvWrRUQMetxzzz2lH5xR50J/L/1fwpb/60L30sGDB4vbbrutuOKKK4opU6YUa9asKT744IMST81odaH76fHHHy+++MUvFldccUUxadKk4m/+5m+Ko0ePlnhqRpv/+I//+Nj3QN5/j6yyonBPBQAAAHn5jC0AAACpCVsAAABSE7YAAACkJmwBAABITdgCAACQmrAFAAAgNWELAABAasIWAACA1IQtAAAAqQlbAAAAUhO2AAAApCZsAQAASE3YAgAAkJqwBQAAIDVhCwAAQGrCFgAAgNSELQAAAKkJWwAAAFITtgAAAKQmbAEAAEhN2AIAAJCasAUAACA1YQsAAEBqwhYAAIDUhC0AAACpCVsAAABSE7YAAACkJmwBAABITdgCAACQmrAFAAAgNWELAABAasIWAACA1IQtAAAAqQlbAAAAUhO2AAAApCZsAQAASE3YAgAAkJqwBQAAIDVhCwAAQGrCFgAAgNSELQAAAKkJWwAAAFITtgAAAKQmbAEAAEhN2AIAAJCasAUAACA1YQsAAEBqwhYAAIDUhC0AAACpCVsAAABSE7YAAACkJmwBAABITdgCAACQmrAFAAAgNWELAABAasIWAACA1IQtAAAAqQlbAAAAUhO2AAAApCZsAQAASE3YAgAAkJqwBQAAIDVhCwAAQGrCFgAAgNSELQAAAKkJWwAAAFITtgAAAKQmbAEAAEhN2AIAAJCasAUAACA1YQsAAEBqwhYAAIDUhC0AAACpCVsAAABSE7YAAACkJmwBAABITdgCAACQmrAFAAAgNWELAABAasIWAACA1IQtAAAAqQlbAAAAUhO2AAAApCZsAQAASE3YAgAAkJqwBQAAIDVhCwAAQGrCFgAAgNT+H/olNl1oxjotAAAAAElFTkSuQmCC", + "text/html": [ + "\n", + "
\n", + "
\n", + " Figure\n", + "
\n", + " \n", + "
\n", + " " + ], + "text/plain": [ + "Canvas(toolbar=Toolbar(toolitems=[('Home', 'Reset original view', 'home', 'home'), ('Back', 'Back to previous …" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'gswfluxdn_measured'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/TBSMERGED/TBSMERGED_tutorial.ipynb b/VAPs/quicklook/TBSMERGED/TBSMERGED_tutorial.ipynb new file mode 100644 index 00000000..5ffca6bd --- /dev/null +++ b/VAPs/quicklook/TBSMERGED/TBSMERGED_tutorial.ipynb @@ -0,0 +1,838 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# TBSMERGED.C1 Notebook\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/tbsmerged) for more information about this vap." + ] + }, + { + "cell_type": "markdown", + "id": "97097763", + "metadata": {}, + "source": [ + "In this notebook, we demonstrate the workflow to explore ARM vap data (using tbsmerged as an example.) Value-added products (VAPs) are higher-order data products that have been analyzed and processed to ease scientists’ use of ARM data in atmospheric research and global climate models. \n", + "Here is the main content we will cover." + ] + }, + { + "cell_type": "markdown", + "id": "eddec40f", + "metadata": {}, + "source": [ + "# Table of Content\n", + "## Access the data\n", + "* How to retrieve the data\n", + "* Data path and file name conventions\n", + "* Load data\n", + "## Explore the data\n", + "* NetCDF Data structure\n", + "* Xarray essentials\n", + "* Xarray Variable\n", + "## Plot the data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "import random\n", + "\n", + "import glob\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "2372d5be", + "metadata": {}, + "source": [ + "## Access the data" + ] + }, + { + "cell_type": "markdown", + "id": "5b0d9684", + "metadata": {}, + "source": [ + "### How to retrieve the data\n", + "We assume the path \"/data/archive\" is available where you are running this notebook. You can use `os.path.exists(\"/data/archive\")` to verify if the path exists at your machine.\n", + "\n", + "\n", + "### Data path and file name conventions\n", + "There several common terminologies regarding ARM data, for example, data-stream-name, data-level, site, facility, instrument, etc. (For more details, please see [ARM Data File Standards Version 1.2](https://www.google.com/search?q=arm+datastream+facility+cite+definition&rlz=1C1GCEJ_enUS1029US1029&ei=hb41ZICrBPukqtsPvMODIA&ved=0ahUKEwjAgeiS0aL-AhV7kmoFHbzhAAQQ4dUDCBA&uact=5&oq=arm+datastream+facility+cite+definition&gs_lcp=Cgxnd3Mtd2l6LXNlcnAQAzIFCAAQogQyBQgAEKIEMgUIABCiBDIFCAAQogQyBQgAEKIEOgoIABBHENYEELADOgoIIRCgARDDBBAKSgQIQRgAUJ4DWIoOYO0PaAFwAXgAgAGMAYgBuQmSAQMyLjmYAQCgAQHIAQjAAQE&sclient=gws-wiz-serp).) \n", + "\n", + "For example, this notebook is called `tbsmerged.c1`, where `tbsmerged` is the \"datastream name\", and the `{process.ds_class_level}}` is the \"data level\".\n", + "\n", + "This datastream also contains site `guc` and facility `S4`. (Note: individual datastream might have multiple site-facility pairs.)\n", + "In such a case, the data of this data-stream is stored at `/data/archive/guc/guctbsmergedS4.c1`, which is in the format of `//.`. We can use the following method to assign the data-stream directory `datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )`\n", + "\n", + "The data files under datastream_dir also follows naming conventions. But once reach the datastream_dir level, the most import file naming convention to differentiate the files is \"yyyyMMdd.hhmmss\", which comes handy to filter out files based on datetime. For example, we can use `glob.glob(f'{datastream_dir}/*.200709*.*')` to filter files in 2007 September.\n", + "\n", + "(Note: refer to the https://adc.arm.gov/discovery/#/ and https://adc.arm.gov/solr8/metadata/select API to explore ARM datastream and assoicated available sites and facilities\n", + "\n", + "Please see the following examples in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d7e9eb85", + "metadata": {}, + "outputs": [], + "source": [ + "# Verify if DATA_DIR path exists\n", + "DATA_DIR = \"/data/archive\"\n", + "os.path.exists(DATA_DIR)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "586993fd", + "metadata": {}, + "outputs": [], + "source": [ + "# Speicify datastream_dir following the path conventions and check its existence\n", + "DATASTREAM_NAME = \"tbsmerged\"\n", + "DATA_LEVEL = \"c1\"\n", + "site = \"guc\"\n", + "facility = \"S4\"\n", + "datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )\n", + "print(datastream_dir)\n", + "print(os.path.exists(datastream_dir))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0742f7c1", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: list 5 (random) files under datastream_dir\n", + "files = os.listdir(datastream_dir)\n", + "files[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39b98a36", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: get most recent file\n", + "list_of_files = glob.glob(f\"{datastream_dir}/*\") # * means all if need specific format then *.csv\n", + "latest_file = max(list_of_files, key=os.path.getctime)\n", + "latest_file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "902d514e", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus sort datastream files based on datetime\n", + "files = os.listdir(datastream_dir)\n", + "file_sorted = files.copy()\n", + "file_sorted.sort() \n", + "print(file_sorted[:5])\n", + "\n", + "# to reverse\n", + "file_sorted_reverse = files.copy() \n", + "file_sorted_reverse.sort(reverse=True)\n", + "print(file_sorted_reverse[:5])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec5923b2", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: pattern matching\n", + "# filter the 200709** files under datastream_dir\n", + "files_filter = glob.glob(f'{datastream_dir}/*.200709*.*')\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c4a4aa18", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: use armnotebook_utils.file_filter (TODO) to filter files based on datastream info \n" + ] + }, + { + "cell_type": "markdown", + "id": "cfeb9efc", + "metadata": {}, + "source": [ + "### Load data\n", + "The arm data is stored in [NetCDF](https://en.wikipedia.org/wiki/NetCDF#:~:text=%22NetCDF%20(network%20Common%20Data%20Form,format%20for%20representing%20scientific%20data.) format. We can use xarray's `open_dataset` method to load single file, or `open_mfdataset` to open multiple files. (Note: the latter will still return a single xarray dataset object by combining multiple files.)\n", + "\n", + "Note: open_dataset keeps the file handle open and lazy loads its contents. All parameters are passed directly to open_dataset. It is a preferable method over load_dataset for memory-effiency. (more details, please see [xarray.open_dataset](https://docs.xarray.dev/en/stable/generated/xarray.open_dataset.html))\n", + "\n", + "See the following example in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a440a329", + "metadata": {}, + "outputs": [], + "source": [ + "# open single file\n", + "full_path = latest_file\n", + "print(full_path)\n", + "ds_single = xr.open_dataset(full_path)\n", + "ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0143a3d", + "metadata": {}, + "outputs": [], + "source": [ + "print(ds_single)\n", + "print(type(ds_single))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c0f8939", + "metadata": {}, + "outputs": [], + "source": [ + "# open multiple files\n", + "n_files = 3\n", + "full_paths = [os.path.join(datastream_dir, f_path) for f_path in file_sorted_reverse[:n_files]]\n", + "print(full_paths)\n", + "try: # Note: sometimes multiple files cannot be merged, so we used try except here.\n", + " ds_mutiple = xr.open_mfdataset(full_paths)\n", + " # ds_mutiple\n", + " print(type(ds_mutiple))\n", + " print(ds_mutiple)\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "3fa59943", + "metadata": {}, + "source": [ + "## Explore the data" + ] + }, + { + "cell_type": "markdown", + "id": "571f69b9", + "metadata": {}, + "source": [ + "### NetCDF Data structure \n", + "\n", + "\n", + "(If you are confident with NetCDF basics and xarray essentials, feel free to skip this session.)\n", + "Before we dive into data exploration, there are some eseential concpets we should be familiar with (shown below): \n", + "* Dataset\n", + "* Data array\n", + "* Variable\n", + "* Dimenssion\n", + "* Coordinate\n", + "* Data Type\n", + "* Meta Data (Attributes)\n", + "\n", + "We will not go into details about NetCDF basics and here are some references you might find helpful\n", + "* [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#)\n", + "* [Network Common Data Form (NetCDF)](https://www.unidata.ucar.edu/software/netcdf/) and [A Brief History of (netCDF) Time](https://www.unidata.ucar.edu/software/netcdf/time/recs.html)\n", + "\n", + "\n", + "\n", + "### Xarray essentials\n", + "Earlier we introduced the xarray pacakge and used `open_dataset` and `open_mfdataset` to retrieve NetCDF data file as an xarray Dataset object. Recall that you can review the data in a notebook by using `print(ds)` or `ds`. \n", + "\n", + "Using xarray to retrieve the aforementioned NetCDF basics is straightforwared. In general,\n", + "* Dataset: `ds`\n", + "* Data array: `ds.variables`\n", + "* Variable: `ds.ds.variables`\n", + "* Dimenssion: `ds.dims`\n", + "* Coordinate: `ds.coords`\n", + "* Data Type: `type`\n", + "* Meta Data (Attributes)\n", + "\n", + "Also, here are some references if you are new to xarray\n", + "* [Xarray in 45 minutes](https://tutorial.xarray.dev/overview/xarray-in-45-min.html)\n", + "* [Handling NetCDF Files using XArray for Absolute Beginners](https://towardsdatascience.com/handling-netcdf-files-using-xarray-for-absolute-beginners-111a8ab4463f)\n", + "\n", + "Try the following commands in action\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b3eac323", + "metadata": {}, + "outputs": [], + "source": [ + "ds = ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77ecf85d", + "metadata": {}, + "outputs": [], + "source": [ + "# Dataset \n", + "ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25e7de09", + "metadata": {}, + "outputs": [], + "source": [ + "# Data array, Variable\n", + "# Note: the info can be overwhelming. Do not attempt to grasp everything at the first glance.\n", + "ds.variables" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c41b67e", + "metadata": {}, + "outputs": [], + "source": [ + "# dimenssions\n", + "ds.dims" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "156f1dfc", + "metadata": {}, + "outputs": [], + "source": [ + "# coordinates\n", + "ds.coords" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "277d6064", + "metadata": {}, + "outputs": [], + "source": [ + "# Meta Data (Attributes)\n", + "ds.attrs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d334681f", + "metadata": {}, + "outputs": [], + "source": [ + "# type\n", + "print(type(ds))\n", + "print(type(ds.variables))\n", + "print(type(ds.dims))\n", + "print(type(ds.coords))\n", + "print(type(ds.attrs))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "643399d6", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: retrieve variable names only\n", + "list(ds.variables)" + ] + }, + { + "cell_type": "markdown", + "id": "1cf36878", + "metadata": {}, + "source": [ + "#### Discussion: variable vs. coordinates vs. dimenssions. \n", + "From [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#):\n", + "\n", + "* Dimession: A dimension may be used to represent a real physical dimension, for example, time, latitude, longitude, or height. A dimension might also be used to index other quantities, for example station or model-run-number.\n", + "A netCDF dimension has both a name and a length. A dimension length is an arbitrary positive integer, except that one dimension in a netCDF dataset can have the length UNLIMITED.\n", + "\n", + "* Variables: Variables are used to store the bulk of the data in a netCDF dataset. A variable represents an array of values of the same type. A scalar value is treated as a 0-dimensional array. A variable has a name, a data type, and a shape described by its list of dimensions specified when the variable is created. A variable may also have associated attributes, which may be added, deleted or changed after the variable is created.\n", + "\n", + "* Coordinate (Variables): It is legal for a variable to have the same name as a dimension. Such variables have no special meaning to the netCDF library. However there is a convention that such variables should be treated in a special way by software using this library.\n", + "A variable with the same name as a dimension is called a coordinate variable. It typically defines a physical coordinate corresponding to that dimension.\n", + "\n", + "\n", + "Tips: By definition, coordinates and dimenssions are also variable. For example ds.time is a coordinate but it is also a special variable. But in practice, when we talk about variable, it implies regular/non-coordinate variable. We will use this convention for the remaining notebook. " + ] + }, + { + "cell_type": "markdown", + "id": "d47120f9", + "metadata": {}, + "source": [ + "### Xarray Variable\n", + "\n", + "\n", + "We can use `ds.variables` to access varialbes of a dataset. But many times viewing print-out the whole datasets can be overwhemling. Instead, we would work on the individual variable (also called Xarray data array.) using the `ds[var_name]` syntax, where `var_name` is the name of the variable. See the following examples in action.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5a57e136", + "metadata": {}, + "outputs": [], + "source": [ + "print(type(ds[\"time\"]))\n", + "ds[\"time\"]" + ] + }, + { + "cell_type": "markdown", + "id": "51ec643e", + "metadata": {}, + "source": [ + "#### Variable properties\n", + "For individual varible, the following properties are mostly used with the assocaited xarray method to retrieve them. (assuming var = ds[var_name])\n", + "* name: `var.name`\n", + "* data content: `var.data`\n", + "* attributes: `var.attrs`\n", + "* dimenstions: `var.dims`\n", + "* data type: `var.data.dtype`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a632a525", + "metadata": {}, + "outputs": [], + "source": [ + "var_name = \"time\"\n", + "var = ds[var_name]\n", + "\n", + "print(\"var.name: \\n\", var.name, \"\\n\")\n", + "print(\"var.data: \\n\", var.data, \"\\n\")\n", + "print(\"var.attrs: \\n\", var.attrs, \"\\n\")\n", + "print(\"var.dims: \\n\", var.dims, \"\\n\")\n", + "print(\"var.data.dtype: \\n\", var.data.dtype, \"\\n\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b256b07f", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: indivdual variable info table (collect and display variable info as a tabular format)\n", + "df_info = pd.DataFrame()\n", + "df_info[\"var_name\"] = list(ds.variables)\n", + "df_info[\"dims\"] = df_info.var_name.apply(lambda x: ds[x].dims)\n", + "df_info[\"is_dim\"] = df_info.var_name.apply(lambda x: x in ds.dims)\n", + "df_info[\"n_dim\"] = df_info.var_name.apply(lambda x: len(ds[x].dims))\n", + "df_info[\"attrs\"] = df_info.var_name.apply(lambda x: ds[x].attrs)\n", + "df_info[\"dtype\"] = df_info.var_name.apply(lambda x: ds[x].data.dtype)\n", + "df_info" + ] + }, + { + "cell_type": "markdown", + "id": "e6a36b96", + "metadata": {}, + "source": [ + "### Data cleaning/Preprocessing (skipped)\n", + "Data cleaning and preprocessing is an important stage in the the data analysis pipeline. However it will be out of the scope of this notebook. For more information about data cleaning and preprocessing basics, here are some references. \n", + "* [Xarray Fundamentals](https://earth-env-data-science.github.io/lectures/xarray/xarray.html)\n", + "* [Xarray Tutorial — Pangeo Gallery documentation](http://gallery.pangeo.io/repos/pangeo-data/pangeo-tutorial-gallery/xarray.html)\n", + "* [Pythonic Data Cleaning With pandas and NumPy](https://realpython.com/python-data-cleaning-numpy-pandas/)\n", + "* [Pandas - Cleaning Data](https://www.w3schools.com/python/pandas/pandas_cleaning.asp)" + ] + }, + { + "cell_type": "markdown", + "id": "ae6e1a69", + "metadata": {}, + "source": [ + "### Plotting\n", + "Data visualization (or plotting) is another important data analysis topic and deserves its own discussion. Here in this notebook we will only demonstrate basic tool for simple data visualization tasks.\n", + "\n", + "Here are some reference you might find useful:\n", + "* xarray plotting: https://docs.xarray.dev/en/stable/user-guide/plotting.html\n", + "* Atmospheric Community Toolkit (ACT): https://arm-doe.github.io/ACT/index.html\n", + "\n", + "Note: this notebook is auto-generated using a template. It uses a general idea to select variable(s) to plot and is not customized to each indivdual notebook. Feel free to change the variables in intrrests, especially certain figure is failed to plot." + ] + }, + { + "cell_type": "markdown", + "id": "7e417fb2", + "metadata": {}, + "source": [ + "#### 1-dimenssional basic time series plot\n", + "\n", + "For the following plot we would like to find variables such that\n", + "* it has one and only one dimession\n", + "* \"time\" is its coordinate variable\n", + "* it is not a dimenssion itself,\n", + "* it is not a special variable with substrings within [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "\n", + "Please see the following example in action. (For more details about pandas filtering, please see the following references.)\n", + "* [pandas: multiple conditions while indexing data frame](https://stackoverflow.com/questions/22591174/pandas-multiple-conditions-while-indexing-data-frame-unexpected-behavior)\n", + "* [pandas.DataFrame.apply](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.apply.html)\n", + "* [How to test if a string contains one of the substrings in a list...](https://stackoverflow.com/questions/26577516/how-to-test-if-a-string-contains-one-of-the-substrings-in-a-list-in-pandas" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30edac2d", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==1, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter = df_info[(df_info.n_dim == 1) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4777a659", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_1d = df_filter.var_name.values[0]\n", + " var_1d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb5ae985", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " ds[var_1d].plot()\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "9e41066e", + "metadata": {}, + "source": [ + "#### 2-dimenssional basic plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09bd4adc", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==2, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_2 = df_info[(df_info.n_dim == 2) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2cdea1a9", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_2d = df_filter_2.var_name.values[0]\n", + " var_2d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5f48879", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " print(ds[var_2d].dims)\n", + " # ds[var_2d].plot()\n", + "\n", + " # conventionally, use \"time\" as x-axis\n", + " ds[var_2d].plot(x=\"time\")\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "7554b2a6", + "metadata": {}, + "source": [ + "#### qc-plotting (optional)\n", + "\n", + "Note: act qc plotting has more strict requirement, one of them is the associated qc variable need to have \"flag_masks\" attributes. Which is added by using ds = act.io.armfiles.read_netcdf(files_list), then ds.clean.cleanup()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71c2096f", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " ds_act = act.io.armfiles.read_netcdf(full_path)\n", + " print(type(ds_act))\n", + " ds_act.clean.cleanup()\n", + "\n", + " # or \n", + " # ds.clean.cleanup()\n", + "except Exception as e:\n", + " print(\"ERROR\", e)\n", + " ds_act = ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "844f8505", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter valid variables for ACT qc plotting\n", + "condition = (\"qc_\" + df_info.var_name).apply(lambda x: ds_act[x].attrs.get(\"flag_masks\") is not None \n", + " if x in list(ds.variables) else False)\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_3 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings))) &\n", + " condition\n", + " ]\n", + "df_filter_3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd38cc6", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "try:\n", + " qc_variable = df_filter_3.var_name.values[0]\n", + " print(qc_variable)\n", + "except Exception as e:\n", + " print(e)\n", + " \n", + "try:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + " qc_ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "d76e4d27", + "metadata": {}, + "source": [ + "#### bonus: choose variables to plot from a dropdown menu " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb733804", + "metadata": {}, + "outputs": [], + "source": [ + "# Valid variables filtering\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_4 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82d5c20d", + "metadata": {}, + "outputs": [], + "source": [ + "# example 1: using xarray plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2fc9aaa4", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# fig, ax = plt.subplots(figsize=(10, 4))\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "# fig.clear() # Remove old lines from plot and plot new one\n", + "# if len(ds[var].dims)==2:\n", + "# ds[var].plot(x=\"time\", add_colorbar=False)\n", + "# else:\n", + "# ds[var].plot()\n", + "# plt.grid()\n", + "# plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fabdd802", + "metadata": {}, + "outputs": [], + "source": [ + "# example 2: using act plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "962b4186", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "\n", + "\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "\n", + "# i_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + "# i_display.add_subplots((1,), figsize=(10, 4))\n", + "# ax = i_display.plot(var, subplot_index=(0,), set_title=f\"{var} ({ds_act[var].attrs['long_name']})\",)\n", + "\n", + "# ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "# ax.grid()\n", + "# plt.show()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/TBSMERGED/tbsmerged.c1.ipynb b/VAPs/quicklook/TBSMERGED/tbsmerged.c1.ipynb new file mode 100644 index 00000000..e0216b35 --- /dev/null +++ b/VAPs/quicklook/TBSMERGED/tbsmerged.c1.ipynb @@ -0,0 +1,339 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# TBSMERGED.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/tbsmerged) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'tbsmerged'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2023-06-16', 'facility': 'S4', 'site': 'guc', 'start_date': '2023-01-21'}, {'end_date': '2022-09-14', 'facility': 'S3', 'site': 'hou', 'start_date': '2022-06-03'}, {'end_date': '2023-07-16', 'facility': 'C1', 'site': 'sgp', 'start_date': '2019-04-25'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2023-07-14'\n", + "date_end = '2023-07-16'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['tbscpc_total_concentration', 'tbsimet_air_temperature', 'tbsimet_rh']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'tbscpc_total_concentration'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'tbscpc_total_concentration'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/TBSMERGED/tbsmergedincloud.c1.ipynb b/VAPs/quicklook/TBSMERGED/tbsmergedincloud.c1.ipynb new file mode 100644 index 00000000..97b44116 --- /dev/null +++ b/VAPs/quicklook/TBSMERGED/tbsmergedincloud.c1.ipynb @@ -0,0 +1,339 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# TBSMERGEDINCLOUD.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/tbsmerged) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'tbsmergedincloud'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2020-11-20', 'facility': 'M1', 'site': 'oli', 'start_date': '2017-04-09'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'oli', 'M1' )\n", + "\n", + "date_start = '2020-11-18'\n", + "date_end = '2020-11-20'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['bl_height_1', 'first_cbh', 'tbscpc_total_concentration']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'bl_height_1'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'bl_height_1'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/TDMA/TDMA_tutorial.ipynb b/VAPs/quicklook/TDMA/TDMA_tutorial.ipynb new file mode 100644 index 00000000..a4748c35 --- /dev/null +++ b/VAPs/quicklook/TDMA/TDMA_tutorial.ipynb @@ -0,0 +1,838 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# TDMAAPSSIZE.C1 Notebook\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/tdma) for more information about this vap." + ] + }, + { + "cell_type": "markdown", + "id": "97097763", + "metadata": {}, + "source": [ + "In this notebook, we demonstrate the workflow to explore ARM vap data (using tdmaapssize as an example.) Value-added products (VAPs) are higher-order data products that have been analyzed and processed to ease scientists’ use of ARM data in atmospheric research and global climate models. \n", + "Here is the main content we will cover." + ] + }, + { + "cell_type": "markdown", + "id": "eddec40f", + "metadata": {}, + "source": [ + "# Table of Content\n", + "## Access the data\n", + "* How to retrieve the data\n", + "* Data path and file name conventions\n", + "* Load data\n", + "## Explore the data\n", + "* NetCDF Data structure\n", + "* Xarray essentials\n", + "* Xarray Variable\n", + "## Plot the data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "import random\n", + "\n", + "import glob\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "2372d5be", + "metadata": {}, + "source": [ + "## Access the data" + ] + }, + { + "cell_type": "markdown", + "id": "5b0d9684", + "metadata": {}, + "source": [ + "### How to retrieve the data\n", + "We assume the path \"/data/archive\" is available where you are running this notebook. You can use `os.path.exists(\"/data/archive\")` to verify if the path exists at your machine.\n", + "\n", + "\n", + "### Data path and file name conventions\n", + "There several common terminologies regarding ARM data, for example, data-stream-name, data-level, site, facility, instrument, etc. (For more details, please see [ARM Data File Standards Version 1.2](https://www.google.com/search?q=arm+datastream+facility+cite+definition&rlz=1C1GCEJ_enUS1029US1029&ei=hb41ZICrBPukqtsPvMODIA&ved=0ahUKEwjAgeiS0aL-AhV7kmoFHbzhAAQQ4dUDCBA&uact=5&oq=arm+datastream+facility+cite+definition&gs_lcp=Cgxnd3Mtd2l6LXNlcnAQAzIFCAAQogQyBQgAEKIEMgUIABCiBDIFCAAQogQyBQgAEKIEOgoIABBHENYEELADOgoIIRCgARDDBBAKSgQIQRgAUJ4DWIoOYO0PaAFwAXgAgAGMAYgBuQmSAQMyLjmYAQCgAQHIAQjAAQE&sclient=gws-wiz-serp).) \n", + "\n", + "For example, this notebook is called `tdmaapssize.c1`, where `tdmaapssize` is the \"datastream name\", and the `{process.ds_class_level}}` is the \"data level\".\n", + "\n", + "This datastream also contains site `sgp` and facility `C1`. (Note: individual datastream might have multiple site-facility pairs.)\n", + "In such a case, the data of this data-stream is stored at `/data/archive/sgp/sgptdmaapssizeC1.c1`, which is in the format of `//.`. We can use the following method to assign the data-stream directory `datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )`\n", + "\n", + "The data files under datastream_dir also follows naming conventions. But once reach the datastream_dir level, the most import file naming convention to differentiate the files is \"yyyyMMdd.hhmmss\", which comes handy to filter out files based on datetime. For example, we can use `glob.glob(f'{datastream_dir}/*.200709*.*')` to filter files in 2007 September.\n", + "\n", + "(Note: refer to the https://adc.arm.gov/discovery/#/ and https://adc.arm.gov/solr8/metadata/select API to explore ARM datastream and assoicated available sites and facilities\n", + "\n", + "Please see the following examples in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d7e9eb85", + "metadata": {}, + "outputs": [], + "source": [ + "# Verify if DATA_DIR path exists\n", + "DATA_DIR = \"/data/archive\"\n", + "os.path.exists(DATA_DIR)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "586993fd", + "metadata": {}, + "outputs": [], + "source": [ + "# Speicify datastream_dir following the path conventions and check its existence\n", + "DATASTREAM_NAME = \"tdmaapssize\"\n", + "DATA_LEVEL = \"c1\"\n", + "site = \"sgp\"\n", + "facility = \"C1\"\n", + "datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )\n", + "print(datastream_dir)\n", + "print(os.path.exists(datastream_dir))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0742f7c1", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: list 5 (random) files under datastream_dir\n", + "files = os.listdir(datastream_dir)\n", + "files[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39b98a36", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: get most recent file\n", + "list_of_files = glob.glob(f\"{datastream_dir}/*\") # * means all if need specific format then *.csv\n", + "latest_file = max(list_of_files, key=os.path.getctime)\n", + "latest_file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "902d514e", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus sort datastream files based on datetime\n", + "files = os.listdir(datastream_dir)\n", + "file_sorted = files.copy()\n", + "file_sorted.sort() \n", + "print(file_sorted[:5])\n", + "\n", + "# to reverse\n", + "file_sorted_reverse = files.copy() \n", + "file_sorted_reverse.sort(reverse=True)\n", + "print(file_sorted_reverse[:5])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec5923b2", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: pattern matching\n", + "# filter the 200709** files under datastream_dir\n", + "files_filter = glob.glob(f'{datastream_dir}/*.200709*.*')\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c4a4aa18", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: use armnotebook_utils.file_filter (TODO) to filter files based on datastream info \n" + ] + }, + { + "cell_type": "markdown", + "id": "cfeb9efc", + "metadata": {}, + "source": [ + "### Load data\n", + "The arm data is stored in [NetCDF](https://en.wikipedia.org/wiki/NetCDF#:~:text=%22NetCDF%20(network%20Common%20Data%20Form,format%20for%20representing%20scientific%20data.) format. We can use xarray's `open_dataset` method to load single file, or `open_mfdataset` to open multiple files. (Note: the latter will still return a single xarray dataset object by combining multiple files.)\n", + "\n", + "Note: open_dataset keeps the file handle open and lazy loads its contents. All parameters are passed directly to open_dataset. It is a preferable method over load_dataset for memory-effiency. (more details, please see [xarray.open_dataset](https://docs.xarray.dev/en/stable/generated/xarray.open_dataset.html))\n", + "\n", + "See the following example in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a440a329", + "metadata": {}, + "outputs": [], + "source": [ + "# open single file\n", + "full_path = latest_file\n", + "print(full_path)\n", + "ds_single = xr.open_dataset(full_path)\n", + "ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0143a3d", + "metadata": {}, + "outputs": [], + "source": [ + "print(ds_single)\n", + "print(type(ds_single))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c0f8939", + "metadata": {}, + "outputs": [], + "source": [ + "# open multiple files\n", + "n_files = 3\n", + "full_paths = [os.path.join(datastream_dir, f_path) for f_path in file_sorted_reverse[:n_files]]\n", + "print(full_paths)\n", + "try: # Note: sometimes multiple files cannot be merged, so we used try except here.\n", + " ds_mutiple = xr.open_mfdataset(full_paths)\n", + " # ds_mutiple\n", + " print(type(ds_mutiple))\n", + " print(ds_mutiple)\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "3fa59943", + "metadata": {}, + "source": [ + "## Explore the data" + ] + }, + { + "cell_type": "markdown", + "id": "571f69b9", + "metadata": {}, + "source": [ + "### NetCDF Data structure \n", + "\n", + "\n", + "(If you are confident with NetCDF basics and xarray essentials, feel free to skip this session.)\n", + "Before we dive into data exploration, there are some eseential concpets we should be familiar with (shown below): \n", + "* Dataset\n", + "* Data array\n", + "* Variable\n", + "* Dimenssion\n", + "* Coordinate\n", + "* Data Type\n", + "* Meta Data (Attributes)\n", + "\n", + "We will not go into details about NetCDF basics and here are some references you might find helpful\n", + "* [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#)\n", + "* [Network Common Data Form (NetCDF)](https://www.unidata.ucar.edu/software/netcdf/) and [A Brief History of (netCDF) Time](https://www.unidata.ucar.edu/software/netcdf/time/recs.html)\n", + "\n", + "\n", + "\n", + "### Xarray essentials\n", + "Earlier we introduced the xarray pacakge and used `open_dataset` and `open_mfdataset` to retrieve NetCDF data file as an xarray Dataset object. Recall that you can review the data in a notebook by using `print(ds)` or `ds`. \n", + "\n", + "Using xarray to retrieve the aforementioned NetCDF basics is straightforwared. In general,\n", + "* Dataset: `ds`\n", + "* Data array: `ds.variables`\n", + "* Variable: `ds.ds.variables`\n", + "* Dimenssion: `ds.dims`\n", + "* Coordinate: `ds.coords`\n", + "* Data Type: `type`\n", + "* Meta Data (Attributes)\n", + "\n", + "Also, here are some references if you are new to xarray\n", + "* [Xarray in 45 minutes](https://tutorial.xarray.dev/overview/xarray-in-45-min.html)\n", + "* [Handling NetCDF Files using XArray for Absolute Beginners](https://towardsdatascience.com/handling-netcdf-files-using-xarray-for-absolute-beginners-111a8ab4463f)\n", + "\n", + "Try the following commands in action\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b3eac323", + "metadata": {}, + "outputs": [], + "source": [ + "ds = ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77ecf85d", + "metadata": {}, + "outputs": [], + "source": [ + "# Dataset \n", + "ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25e7de09", + "metadata": {}, + "outputs": [], + "source": [ + "# Data array, Variable\n", + "# Note: the info can be overwhelming. Do not attempt to grasp everything at the first glance.\n", + "ds.variables" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c41b67e", + "metadata": {}, + "outputs": [], + "source": [ + "# dimenssions\n", + "ds.dims" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "156f1dfc", + "metadata": {}, + "outputs": [], + "source": [ + "# coordinates\n", + "ds.coords" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "277d6064", + "metadata": {}, + "outputs": [], + "source": [ + "# Meta Data (Attributes)\n", + "ds.attrs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d334681f", + "metadata": {}, + "outputs": [], + "source": [ + "# type\n", + "print(type(ds))\n", + "print(type(ds.variables))\n", + "print(type(ds.dims))\n", + "print(type(ds.coords))\n", + "print(type(ds.attrs))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "643399d6", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: retrieve variable names only\n", + "list(ds.variables)" + ] + }, + { + "cell_type": "markdown", + "id": "1cf36878", + "metadata": {}, + "source": [ + "#### Discussion: variable vs. coordinates vs. dimenssions. \n", + "From [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#):\n", + "\n", + "* Dimession: A dimension may be used to represent a real physical dimension, for example, time, latitude, longitude, or height. A dimension might also be used to index other quantities, for example station or model-run-number.\n", + "A netCDF dimension has both a name and a length. A dimension length is an arbitrary positive integer, except that one dimension in a netCDF dataset can have the length UNLIMITED.\n", + "\n", + "* Variables: Variables are used to store the bulk of the data in a netCDF dataset. A variable represents an array of values of the same type. A scalar value is treated as a 0-dimensional array. A variable has a name, a data type, and a shape described by its list of dimensions specified when the variable is created. A variable may also have associated attributes, which may be added, deleted or changed after the variable is created.\n", + "\n", + "* Coordinate (Variables): It is legal for a variable to have the same name as a dimension. Such variables have no special meaning to the netCDF library. However there is a convention that such variables should be treated in a special way by software using this library.\n", + "A variable with the same name as a dimension is called a coordinate variable. It typically defines a physical coordinate corresponding to that dimension.\n", + "\n", + "\n", + "Tips: By definition, coordinates and dimenssions are also variable. For example ds.time is a coordinate but it is also a special variable. But in practice, when we talk about variable, it implies regular/non-coordinate variable. We will use this convention for the remaining notebook. " + ] + }, + { + "cell_type": "markdown", + "id": "d47120f9", + "metadata": {}, + "source": [ + "### Xarray Variable\n", + "\n", + "\n", + "We can use `ds.variables` to access varialbes of a dataset. But many times viewing print-out the whole datasets can be overwhemling. Instead, we would work on the individual variable (also called Xarray data array.) using the `ds[var_name]` syntax, where `var_name` is the name of the variable. See the following examples in action.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5a57e136", + "metadata": {}, + "outputs": [], + "source": [ + "print(type(ds[\"time\"]))\n", + "ds[\"time\"]" + ] + }, + { + "cell_type": "markdown", + "id": "51ec643e", + "metadata": {}, + "source": [ + "#### Variable properties\n", + "For individual varible, the following properties are mostly used with the assocaited xarray method to retrieve them. (assuming var = ds[var_name])\n", + "* name: `var.name`\n", + "* data content: `var.data`\n", + "* attributes: `var.attrs`\n", + "* dimenstions: `var.dims`\n", + "* data type: `var.data.dtype`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a632a525", + "metadata": {}, + "outputs": [], + "source": [ + "var_name = \"time\"\n", + "var = ds[var_name]\n", + "\n", + "print(\"var.name: \\n\", var.name, \"\\n\")\n", + "print(\"var.data: \\n\", var.data, \"\\n\")\n", + "print(\"var.attrs: \\n\", var.attrs, \"\\n\")\n", + "print(\"var.dims: \\n\", var.dims, \"\\n\")\n", + "print(\"var.data.dtype: \\n\", var.data.dtype, \"\\n\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b256b07f", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: indivdual variable info table (collect and display variable info as a tabular format)\n", + "df_info = pd.DataFrame()\n", + "df_info[\"var_name\"] = list(ds.variables)\n", + "df_info[\"dims\"] = df_info.var_name.apply(lambda x: ds[x].dims)\n", + "df_info[\"is_dim\"] = df_info.var_name.apply(lambda x: x in ds.dims)\n", + "df_info[\"n_dim\"] = df_info.var_name.apply(lambda x: len(ds[x].dims))\n", + "df_info[\"attrs\"] = df_info.var_name.apply(lambda x: ds[x].attrs)\n", + "df_info[\"dtype\"] = df_info.var_name.apply(lambda x: ds[x].data.dtype)\n", + "df_info" + ] + }, + { + "cell_type": "markdown", + "id": "e6a36b96", + "metadata": {}, + "source": [ + "### Data cleaning/Preprocessing (skipped)\n", + "Data cleaning and preprocessing is an important stage in the the data analysis pipeline. However it will be out of the scope of this notebook. For more information about data cleaning and preprocessing basics, here are some references. \n", + "* [Xarray Fundamentals](https://earth-env-data-science.github.io/lectures/xarray/xarray.html)\n", + "* [Xarray Tutorial — Pangeo Gallery documentation](http://gallery.pangeo.io/repos/pangeo-data/pangeo-tutorial-gallery/xarray.html)\n", + "* [Pythonic Data Cleaning With pandas and NumPy](https://realpython.com/python-data-cleaning-numpy-pandas/)\n", + "* [Pandas - Cleaning Data](https://www.w3schools.com/python/pandas/pandas_cleaning.asp)" + ] + }, + { + "cell_type": "markdown", + "id": "ae6e1a69", + "metadata": {}, + "source": [ + "### Plotting\n", + "Data visualization (or plotting) is another important data analysis topic and deserves its own discussion. Here in this notebook we will only demonstrate basic tool for simple data visualization tasks.\n", + "\n", + "Here are some reference you might find useful:\n", + "* xarray plotting: https://docs.xarray.dev/en/stable/user-guide/plotting.html\n", + "* Atmospheric Community Toolkit (ACT): https://arm-doe.github.io/ACT/index.html\n", + "\n", + "Note: this notebook is auto-generated using a template. It uses a general idea to select variable(s) to plot and is not customized to each indivdual notebook. Feel free to change the variables in intrrests, especially certain figure is failed to plot." + ] + }, + { + "cell_type": "markdown", + "id": "7e417fb2", + "metadata": {}, + "source": [ + "#### 1-dimenssional basic time series plot\n", + "\n", + "For the following plot we would like to find variables such that\n", + "* it has one and only one dimession\n", + "* \"time\" is its coordinate variable\n", + "* it is not a dimenssion itself,\n", + "* it is not a special variable with substrings within [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "\n", + "Please see the following example in action. (For more details about pandas filtering, please see the following references.)\n", + "* [pandas: multiple conditions while indexing data frame](https://stackoverflow.com/questions/22591174/pandas-multiple-conditions-while-indexing-data-frame-unexpected-behavior)\n", + "* [pandas.DataFrame.apply](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.apply.html)\n", + "* [How to test if a string contains one of the substrings in a list...](https://stackoverflow.com/questions/26577516/how-to-test-if-a-string-contains-one-of-the-substrings-in-a-list-in-pandas" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30edac2d", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==1, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter = df_info[(df_info.n_dim == 1) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4777a659", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_1d = df_filter.var_name.values[0]\n", + " var_1d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb5ae985", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " ds[var_1d].plot()\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "9e41066e", + "metadata": {}, + "source": [ + "#### 2-dimenssional basic plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09bd4adc", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==2, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_2 = df_info[(df_info.n_dim == 2) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2cdea1a9", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_2d = df_filter_2.var_name.values[0]\n", + " var_2d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5f48879", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " print(ds[var_2d].dims)\n", + " # ds[var_2d].plot()\n", + "\n", + " # conventionally, use \"time\" as x-axis\n", + " ds[var_2d].plot(x=\"time\")\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "7554b2a6", + "metadata": {}, + "source": [ + "#### qc-plotting (optional)\n", + "\n", + "Note: act qc plotting has more strict requirement, one of them is the associated qc variable need to have \"flag_masks\" attributes. Which is added by using ds = act.io.armfiles.read_netcdf(files_list), then ds.clean.cleanup()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71c2096f", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " ds_act = act.io.armfiles.read_netcdf(full_path)\n", + " print(type(ds_act))\n", + " ds_act.clean.cleanup()\n", + "\n", + " # or \n", + " # ds.clean.cleanup()\n", + "except Exception as e:\n", + " print(\"ERROR\", e)\n", + " ds_act = ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "844f8505", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter valid variables for ACT qc plotting\n", + "condition = (\"qc_\" + df_info.var_name).apply(lambda x: ds_act[x].attrs.get(\"flag_masks\") is not None \n", + " if x in list(ds.variables) else False)\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_3 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings))) &\n", + " condition\n", + " ]\n", + "df_filter_3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd38cc6", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "try:\n", + " qc_variable = df_filter_3.var_name.values[0]\n", + " print(qc_variable)\n", + "except Exception as e:\n", + " print(e)\n", + " \n", + "try:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + " qc_ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "d76e4d27", + "metadata": {}, + "source": [ + "#### bonus: choose variables to plot from a dropdown menu " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb733804", + "metadata": {}, + "outputs": [], + "source": [ + "# Valid variables filtering\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_4 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82d5c20d", + "metadata": {}, + "outputs": [], + "source": [ + "# example 1: using xarray plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2fc9aaa4", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# fig, ax = plt.subplots(figsize=(10, 4))\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "# fig.clear() # Remove old lines from plot and plot new one\n", + "# if len(ds[var].dims)==2:\n", + "# ds[var].plot(x=\"time\", add_colorbar=False)\n", + "# else:\n", + "# ds[var].plot()\n", + "# plt.grid()\n", + "# plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fabdd802", + "metadata": {}, + "outputs": [], + "source": [ + "# example 2: using act plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "962b4186", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "\n", + "\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "\n", + "# i_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + "# i_display.add_subplots((1,), figsize=(10, 4))\n", + "# ax = i_display.plot(var, subplot_index=(0,), set_title=f\"{var} ({ds_act[var].attrs['long_name']})\",)\n", + "\n", + "# ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "# ax.grid()\n", + "# plt.show()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/TDMA/tdmaapssize.c1.ipynb b/VAPs/quicklook/TDMA/tdmaapssize.c1.ipynb new file mode 100644 index 00000000..02a9cff9 --- /dev/null +++ b/VAPs/quicklook/TDMA/tdmaapssize.c1.ipynb @@ -0,0 +1,339 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# TDMAAPSSIZE.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/tdma) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'tdmaapssize'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2014-11-20', 'facility': 'C1', 'site': 'sgp', 'start_date': '2010-01-01'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2014-11-18'\n", + "date_end = '2014-11-20'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['number_concentration_DMA_APS', 'integrated_number_concentration_DMA']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'number_concentration_DMA_APS'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'number_concentration_DMA_APS'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/TWRMR/1twrmr.c1.ipynb b/VAPs/quicklook/TWRMR/1twrmr.c1.ipynb new file mode 100644 index 00000000..7a36af24 --- /dev/null +++ b/VAPs/quicklook/TWRMR/1twrmr.c1.ipynb @@ -0,0 +1,339 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# 1TWRMR.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/twrmr) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = '1twrmr'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2023-12-17', 'facility': 'C1', 'site': 'sgp', 'start_date': '1998-04-01'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2023-12-15'\n", + "date_end = '2023-12-17'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['pres_02m', 'pres_25m', 'pres_60m']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'pres_02m'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'pres_02m'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/TWRMR/30twrmr.c1.ipynb b/VAPs/quicklook/TWRMR/30twrmr.c1.ipynb new file mode 100644 index 00000000..92c0c433 --- /dev/null +++ b/VAPs/quicklook/TWRMR/30twrmr.c1.ipynb @@ -0,0 +1,297 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# 30TWRMR.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/twrmr) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = '30twrmr'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2009-09-29', 'facility': 'C1', 'site': 'sgp', 'start_date': '1998-04-01'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2009-09-28'\n", + "date_end = '2009-09-29'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['pres_02m', 'pres_25m', 'pres_60m']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'pres_02m'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/TWRMR/TWRMR_tutorial.ipynb b/VAPs/quicklook/TWRMR/TWRMR_tutorial.ipynb new file mode 100644 index 00000000..e13c8a9c --- /dev/null +++ b/VAPs/quicklook/TWRMR/TWRMR_tutorial.ipynb @@ -0,0 +1,838 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# 1TWRMR.C1 Notebook\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/twrmr) for more information about this vap." + ] + }, + { + "cell_type": "markdown", + "id": "97097763", + "metadata": {}, + "source": [ + "In this notebook, we demonstrate the workflow to explore ARM vap data (using 1twrmr as an example.) Value-added products (VAPs) are higher-order data products that have been analyzed and processed to ease scientists’ use of ARM data in atmospheric research and global climate models. \n", + "Here is the main content we will cover." + ] + }, + { + "cell_type": "markdown", + "id": "eddec40f", + "metadata": {}, + "source": [ + "# Table of Content\n", + "## Access the data\n", + "* How to retrieve the data\n", + "* Data path and file name conventions\n", + "* Load data\n", + "## Explore the data\n", + "* NetCDF Data structure\n", + "* Xarray essentials\n", + "* Xarray Variable\n", + "## Plot the data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "import random\n", + "\n", + "import glob\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "2372d5be", + "metadata": {}, + "source": [ + "## Access the data" + ] + }, + { + "cell_type": "markdown", + "id": "5b0d9684", + "metadata": {}, + "source": [ + "### How to retrieve the data\n", + "We assume the path \"/data/archive\" is available where you are running this notebook. You can use `os.path.exists(\"/data/archive\")` to verify if the path exists at your machine.\n", + "\n", + "\n", + "### Data path and file name conventions\n", + "There several common terminologies regarding ARM data, for example, data-stream-name, data-level, site, facility, instrument, etc. (For more details, please see [ARM Data File Standards Version 1.2](https://www.google.com/search?q=arm+datastream+facility+cite+definition&rlz=1C1GCEJ_enUS1029US1029&ei=hb41ZICrBPukqtsPvMODIA&ved=0ahUKEwjAgeiS0aL-AhV7kmoFHbzhAAQQ4dUDCBA&uact=5&oq=arm+datastream+facility+cite+definition&gs_lcp=Cgxnd3Mtd2l6LXNlcnAQAzIFCAAQogQyBQgAEKIEMgUIABCiBDIFCAAQogQyBQgAEKIEOgoIABBHENYEELADOgoIIRCgARDDBBAKSgQIQRgAUJ4DWIoOYO0PaAFwAXgAgAGMAYgBuQmSAQMyLjmYAQCgAQHIAQjAAQE&sclient=gws-wiz-serp).) \n", + "\n", + "For example, this notebook is called `1twrmr.c1`, where `1twrmr` is the \"datastream name\", and the `{process.ds_class_level}}` is the \"data level\".\n", + "\n", + "This datastream also contains site `sgp` and facility `C1`. (Note: individual datastream might have multiple site-facility pairs.)\n", + "In such a case, the data of this data-stream is stored at `/data/archive/sgp/sgp1twrmrC1.c1`, which is in the format of `//.`. We can use the following method to assign the data-stream directory `datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )`\n", + "\n", + "The data files under datastream_dir also follows naming conventions. But once reach the datastream_dir level, the most import file naming convention to differentiate the files is \"yyyyMMdd.hhmmss\", which comes handy to filter out files based on datetime. For example, we can use `glob.glob(f'{datastream_dir}/*.200709*.*')` to filter files in 2007 September.\n", + "\n", + "(Note: refer to the https://adc.arm.gov/discovery/#/ and https://adc.arm.gov/solr8/metadata/select API to explore ARM datastream and assoicated available sites and facilities\n", + "\n", + "Please see the following examples in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d7e9eb85", + "metadata": {}, + "outputs": [], + "source": [ + "# Verify if DATA_DIR path exists\n", + "DATA_DIR = \"/data/archive\"\n", + "os.path.exists(DATA_DIR)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "586993fd", + "metadata": {}, + "outputs": [], + "source": [ + "# Speicify datastream_dir following the path conventions and check its existence\n", + "DATASTREAM_NAME = \"1twrmr\"\n", + "DATA_LEVEL = \"c1\"\n", + "site = \"sgp\"\n", + "facility = \"C1\"\n", + "datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )\n", + "print(datastream_dir)\n", + "print(os.path.exists(datastream_dir))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0742f7c1", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: list 5 (random) files under datastream_dir\n", + "files = os.listdir(datastream_dir)\n", + "files[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39b98a36", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: get most recent file\n", + "list_of_files = glob.glob(f\"{datastream_dir}/*\") # * means all if need specific format then *.csv\n", + "latest_file = max(list_of_files, key=os.path.getctime)\n", + "latest_file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "902d514e", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus sort datastream files based on datetime\n", + "files = os.listdir(datastream_dir)\n", + "file_sorted = files.copy()\n", + "file_sorted.sort() \n", + "print(file_sorted[:5])\n", + "\n", + "# to reverse\n", + "file_sorted_reverse = files.copy() \n", + "file_sorted_reverse.sort(reverse=True)\n", + "print(file_sorted_reverse[:5])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec5923b2", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: pattern matching\n", + "# filter the 200709** files under datastream_dir\n", + "files_filter = glob.glob(f'{datastream_dir}/*.200709*.*')\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c4a4aa18", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: use armnotebook_utils.file_filter (TODO) to filter files based on datastream info \n" + ] + }, + { + "cell_type": "markdown", + "id": "cfeb9efc", + "metadata": {}, + "source": [ + "### Load data\n", + "The arm data is stored in [NetCDF](https://en.wikipedia.org/wiki/NetCDF#:~:text=%22NetCDF%20(network%20Common%20Data%20Form,format%20for%20representing%20scientific%20data.) format. We can use xarray's `open_dataset` method to load single file, or `open_mfdataset` to open multiple files. (Note: the latter will still return a single xarray dataset object by combining multiple files.)\n", + "\n", + "Note: open_dataset keeps the file handle open and lazy loads its contents. All parameters are passed directly to open_dataset. It is a preferable method over load_dataset for memory-effiency. (more details, please see [xarray.open_dataset](https://docs.xarray.dev/en/stable/generated/xarray.open_dataset.html))\n", + "\n", + "See the following example in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a440a329", + "metadata": {}, + "outputs": [], + "source": [ + "# open single file\n", + "full_path = latest_file\n", + "print(full_path)\n", + "ds_single = xr.open_dataset(full_path)\n", + "ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0143a3d", + "metadata": {}, + "outputs": [], + "source": [ + "print(ds_single)\n", + "print(type(ds_single))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c0f8939", + "metadata": {}, + "outputs": [], + "source": [ + "# open multiple files\n", + "n_files = 3\n", + "full_paths = [os.path.join(datastream_dir, f_path) for f_path in file_sorted_reverse[:n_files]]\n", + "print(full_paths)\n", + "try: # Note: sometimes multiple files cannot be merged, so we used try except here.\n", + " ds_mutiple = xr.open_mfdataset(full_paths)\n", + " # ds_mutiple\n", + " print(type(ds_mutiple))\n", + " print(ds_mutiple)\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "3fa59943", + "metadata": {}, + "source": [ + "## Explore the data" + ] + }, + { + "cell_type": "markdown", + "id": "571f69b9", + "metadata": {}, + "source": [ + "### NetCDF Data structure \n", + "\n", + "\n", + "(If you are confident with NetCDF basics and xarray essentials, feel free to skip this session.)\n", + "Before we dive into data exploration, there are some eseential concpets we should be familiar with (shown below): \n", + "* Dataset\n", + "* Data array\n", + "* Variable\n", + "* Dimenssion\n", + "* Coordinate\n", + "* Data Type\n", + "* Meta Data (Attributes)\n", + "\n", + "We will not go into details about NetCDF basics and here are some references you might find helpful\n", + "* [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#)\n", + "* [Network Common Data Form (NetCDF)](https://www.unidata.ucar.edu/software/netcdf/) and [A Brief History of (netCDF) Time](https://www.unidata.ucar.edu/software/netcdf/time/recs.html)\n", + "\n", + "\n", + "\n", + "### Xarray essentials\n", + "Earlier we introduced the xarray pacakge and used `open_dataset` and `open_mfdataset` to retrieve NetCDF data file as an xarray Dataset object. Recall that you can review the data in a notebook by using `print(ds)` or `ds`. \n", + "\n", + "Using xarray to retrieve the aforementioned NetCDF basics is straightforwared. In general,\n", + "* Dataset: `ds`\n", + "* Data array: `ds.variables`\n", + "* Variable: `ds.ds.variables`\n", + "* Dimenssion: `ds.dims`\n", + "* Coordinate: `ds.coords`\n", + "* Data Type: `type`\n", + "* Meta Data (Attributes)\n", + "\n", + "Also, here are some references if you are new to xarray\n", + "* [Xarray in 45 minutes](https://tutorial.xarray.dev/overview/xarray-in-45-min.html)\n", + "* [Handling NetCDF Files using XArray for Absolute Beginners](https://towardsdatascience.com/handling-netcdf-files-using-xarray-for-absolute-beginners-111a8ab4463f)\n", + "\n", + "Try the following commands in action\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b3eac323", + "metadata": {}, + "outputs": [], + "source": [ + "ds = ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77ecf85d", + "metadata": {}, + "outputs": [], + "source": [ + "# Dataset \n", + "ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25e7de09", + "metadata": {}, + "outputs": [], + "source": [ + "# Data array, Variable\n", + "# Note: the info can be overwhelming. Do not attempt to grasp everything at the first glance.\n", + "ds.variables" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c41b67e", + "metadata": {}, + "outputs": [], + "source": [ + "# dimenssions\n", + "ds.dims" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "156f1dfc", + "metadata": {}, + "outputs": [], + "source": [ + "# coordinates\n", + "ds.coords" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "277d6064", + "metadata": {}, + "outputs": [], + "source": [ + "# Meta Data (Attributes)\n", + "ds.attrs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d334681f", + "metadata": {}, + "outputs": [], + "source": [ + "# type\n", + "print(type(ds))\n", + "print(type(ds.variables))\n", + "print(type(ds.dims))\n", + "print(type(ds.coords))\n", + "print(type(ds.attrs))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "643399d6", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: retrieve variable names only\n", + "list(ds.variables)" + ] + }, + { + "cell_type": "markdown", + "id": "1cf36878", + "metadata": {}, + "source": [ + "#### Discussion: variable vs. coordinates vs. dimenssions. \n", + "From [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#):\n", + "\n", + "* Dimession: A dimension may be used to represent a real physical dimension, for example, time, latitude, longitude, or height. A dimension might also be used to index other quantities, for example station or model-run-number.\n", + "A netCDF dimension has both a name and a length. A dimension length is an arbitrary positive integer, except that one dimension in a netCDF dataset can have the length UNLIMITED.\n", + "\n", + "* Variables: Variables are used to store the bulk of the data in a netCDF dataset. A variable represents an array of values of the same type. A scalar value is treated as a 0-dimensional array. A variable has a name, a data type, and a shape described by its list of dimensions specified when the variable is created. A variable may also have associated attributes, which may be added, deleted or changed after the variable is created.\n", + "\n", + "* Coordinate (Variables): It is legal for a variable to have the same name as a dimension. Such variables have no special meaning to the netCDF library. However there is a convention that such variables should be treated in a special way by software using this library.\n", + "A variable with the same name as a dimension is called a coordinate variable. It typically defines a physical coordinate corresponding to that dimension.\n", + "\n", + "\n", + "Tips: By definition, coordinates and dimenssions are also variable. For example ds.time is a coordinate but it is also a special variable. But in practice, when we talk about variable, it implies regular/non-coordinate variable. We will use this convention for the remaining notebook. " + ] + }, + { + "cell_type": "markdown", + "id": "d47120f9", + "metadata": {}, + "source": [ + "### Xarray Variable\n", + "\n", + "\n", + "We can use `ds.variables` to access varialbes of a dataset. But many times viewing print-out the whole datasets can be overwhemling. Instead, we would work on the individual variable (also called Xarray data array.) using the `ds[var_name]` syntax, where `var_name` is the name of the variable. See the following examples in action.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5a57e136", + "metadata": {}, + "outputs": [], + "source": [ + "print(type(ds[\"time\"]))\n", + "ds[\"time\"]" + ] + }, + { + "cell_type": "markdown", + "id": "51ec643e", + "metadata": {}, + "source": [ + "#### Variable properties\n", + "For individual varible, the following properties are mostly used with the assocaited xarray method to retrieve them. (assuming var = ds[var_name])\n", + "* name: `var.name`\n", + "* data content: `var.data`\n", + "* attributes: `var.attrs`\n", + "* dimenstions: `var.dims`\n", + "* data type: `var.data.dtype`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a632a525", + "metadata": {}, + "outputs": [], + "source": [ + "var_name = \"time\"\n", + "var = ds[var_name]\n", + "\n", + "print(\"var.name: \\n\", var.name, \"\\n\")\n", + "print(\"var.data: \\n\", var.data, \"\\n\")\n", + "print(\"var.attrs: \\n\", var.attrs, \"\\n\")\n", + "print(\"var.dims: \\n\", var.dims, \"\\n\")\n", + "print(\"var.data.dtype: \\n\", var.data.dtype, \"\\n\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b256b07f", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: indivdual variable info table (collect and display variable info as a tabular format)\n", + "df_info = pd.DataFrame()\n", + "df_info[\"var_name\"] = list(ds.variables)\n", + "df_info[\"dims\"] = df_info.var_name.apply(lambda x: ds[x].dims)\n", + "df_info[\"is_dim\"] = df_info.var_name.apply(lambda x: x in ds.dims)\n", + "df_info[\"n_dim\"] = df_info.var_name.apply(lambda x: len(ds[x].dims))\n", + "df_info[\"attrs\"] = df_info.var_name.apply(lambda x: ds[x].attrs)\n", + "df_info[\"dtype\"] = df_info.var_name.apply(lambda x: ds[x].data.dtype)\n", + "df_info" + ] + }, + { + "cell_type": "markdown", + "id": "e6a36b96", + "metadata": {}, + "source": [ + "### Data cleaning/Preprocessing (skipped)\n", + "Data cleaning and preprocessing is an important stage in the the data analysis pipeline. However it will be out of the scope of this notebook. For more information about data cleaning and preprocessing basics, here are some references. \n", + "* [Xarray Fundamentals](https://earth-env-data-science.github.io/lectures/xarray/xarray.html)\n", + "* [Xarray Tutorial — Pangeo Gallery documentation](http://gallery.pangeo.io/repos/pangeo-data/pangeo-tutorial-gallery/xarray.html)\n", + "* [Pythonic Data Cleaning With pandas and NumPy](https://realpython.com/python-data-cleaning-numpy-pandas/)\n", + "* [Pandas - Cleaning Data](https://www.w3schools.com/python/pandas/pandas_cleaning.asp)" + ] + }, + { + "cell_type": "markdown", + "id": "ae6e1a69", + "metadata": {}, + "source": [ + "### Plotting\n", + "Data visualization (or plotting) is another important data analysis topic and deserves its own discussion. Here in this notebook we will only demonstrate basic tool for simple data visualization tasks.\n", + "\n", + "Here are some reference you might find useful:\n", + "* xarray plotting: https://docs.xarray.dev/en/stable/user-guide/plotting.html\n", + "* Atmospheric Community Toolkit (ACT): https://arm-doe.github.io/ACT/index.html\n", + "\n", + "Note: this notebook is auto-generated using a template. It uses a general idea to select variable(s) to plot and is not customized to each indivdual notebook. Feel free to change the variables in intrrests, especially certain figure is failed to plot." + ] + }, + { + "cell_type": "markdown", + "id": "7e417fb2", + "metadata": {}, + "source": [ + "#### 1-dimenssional basic time series plot\n", + "\n", + "For the following plot we would like to find variables such that\n", + "* it has one and only one dimession\n", + "* \"time\" is its coordinate variable\n", + "* it is not a dimenssion itself,\n", + "* it is not a special variable with substrings within [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "\n", + "Please see the following example in action. (For more details about pandas filtering, please see the following references.)\n", + "* [pandas: multiple conditions while indexing data frame](https://stackoverflow.com/questions/22591174/pandas-multiple-conditions-while-indexing-data-frame-unexpected-behavior)\n", + "* [pandas.DataFrame.apply](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.apply.html)\n", + "* [How to test if a string contains one of the substrings in a list...](https://stackoverflow.com/questions/26577516/how-to-test-if-a-string-contains-one-of-the-substrings-in-a-list-in-pandas" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30edac2d", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==1, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter = df_info[(df_info.n_dim == 1) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4777a659", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_1d = df_filter.var_name.values[0]\n", + " var_1d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb5ae985", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " ds[var_1d].plot()\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "9e41066e", + "metadata": {}, + "source": [ + "#### 2-dimenssional basic plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09bd4adc", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==2, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_2 = df_info[(df_info.n_dim == 2) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2cdea1a9", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_2d = df_filter_2.var_name.values[0]\n", + " var_2d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5f48879", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " print(ds[var_2d].dims)\n", + " # ds[var_2d].plot()\n", + "\n", + " # conventionally, use \"time\" as x-axis\n", + " ds[var_2d].plot(x=\"time\")\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "7554b2a6", + "metadata": {}, + "source": [ + "#### qc-plotting (optional)\n", + "\n", + "Note: act qc plotting has more strict requirement, one of them is the associated qc variable need to have \"flag_masks\" attributes. Which is added by using ds = act.io.armfiles.read_netcdf(files_list), then ds.clean.cleanup()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71c2096f", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " ds_act = act.io.armfiles.read_netcdf(full_path)\n", + " print(type(ds_act))\n", + " ds_act.clean.cleanup()\n", + "\n", + " # or \n", + " # ds.clean.cleanup()\n", + "except Exception as e:\n", + " print(\"ERROR\", e)\n", + " ds_act = ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "844f8505", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter valid variables for ACT qc plotting\n", + "condition = (\"qc_\" + df_info.var_name).apply(lambda x: ds_act[x].attrs.get(\"flag_masks\") is not None \n", + " if x in list(ds.variables) else False)\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_3 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings))) &\n", + " condition\n", + " ]\n", + "df_filter_3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd38cc6", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "try:\n", + " qc_variable = df_filter_3.var_name.values[0]\n", + " print(qc_variable)\n", + "except Exception as e:\n", + " print(e)\n", + " \n", + "try:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + " qc_ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "d76e4d27", + "metadata": {}, + "source": [ + "#### bonus: choose variables to plot from a dropdown menu " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb733804", + "metadata": {}, + "outputs": [], + "source": [ + "# Valid variables filtering\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_4 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82d5c20d", + "metadata": {}, + "outputs": [], + "source": [ + "# example 1: using xarray plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2fc9aaa4", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# fig, ax = plt.subplots(figsize=(10, 4))\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "# fig.clear() # Remove old lines from plot and plot new one\n", + "# if len(ds[var].dims)==2:\n", + "# ds[var].plot(x=\"time\", add_colorbar=False)\n", + "# else:\n", + "# ds[var].plot()\n", + "# plt.grid()\n", + "# plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fabdd802", + "metadata": {}, + "outputs": [], + "source": [ + "# example 2: using act plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "962b4186", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "\n", + "\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "\n", + "# i_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + "# i_display.add_subplots((1,), figsize=(10, 4))\n", + "# ax = i_display.plot(var, subplot_index=(0,), set_title=f\"{var} ({ds_act[var].attrs['long_name']})\",)\n", + "\n", + "# ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "# ax.grid()\n", + "# plt.show()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/VARANAL/180varanaecmwf.c1.ipynb b/VAPs/quicklook/VARANAL/180varanaecmwf.c1.ipynb new file mode 100644 index 00000000..96a3022c --- /dev/null +++ b/VAPs/quicklook/VARANAL/180varanaecmwf.c1.ipynb @@ -0,0 +1,297 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# 180VARANAECMWF.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/varanal) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = '180varanaecmwf'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2016-05-31', 'facility': 'M1', 'site': 'awr', 'start_date': '2016-01-01'}, {'end_date': '2018-02-28', 'facility': 'C1', 'site': 'ena', 'start_date': '2017-06-01'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'awr', 'M1' )\n", + "\n", + "date_start = '2016-04-29'\n", + "date_end = '2016-05-01'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['T_adv_h', 'T_adv_v', 'q_adv_h']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'T_adv_h'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/VARANAL/180varanamerra001.c1.ipynb b/VAPs/quicklook/VARANAL/180varanamerra001.c1.ipynb new file mode 100644 index 00000000..a523a3bc --- /dev/null +++ b/VAPs/quicklook/VARANAL/180varanamerra001.c1.ipynb @@ -0,0 +1,297 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# 180VARANAMERRA001.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/varanal) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = '180varanamerra001'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2008-11-30', 'facility': 'M1', 'site': 'hfe', 'start_date': '2008-11-01'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'hfe', 'M1' )\n", + "\n", + "date_start = '2008-10-30'\n", + "date_end = '2008-11-01'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['year', 'month', 'day']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'year'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/VARANAL/VARANAL_tutorial.ipynb b/VAPs/quicklook/VARANAL/VARANAL_tutorial.ipynb new file mode 100644 index 00000000..622bb767 --- /dev/null +++ b/VAPs/quicklook/VARANAL/VARANAL_tutorial.ipynb @@ -0,0 +1,838 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# 180VARANAECMWF.C1 Notebook\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/varanal) for more information about this vap." + ] + }, + { + "cell_type": "markdown", + "id": "97097763", + "metadata": {}, + "source": [ + "In this notebook, we demonstrate the workflow to explore ARM vap data (using 180varanaecmwf as an example.) Value-added products (VAPs) are higher-order data products that have been analyzed and processed to ease scientists’ use of ARM data in atmospheric research and global climate models. \n", + "Here is the main content we will cover." + ] + }, + { + "cell_type": "markdown", + "id": "eddec40f", + "metadata": {}, + "source": [ + "# Table of Content\n", + "## Access the data\n", + "* How to retrieve the data\n", + "* Data path and file name conventions\n", + "* Load data\n", + "## Explore the data\n", + "* NetCDF Data structure\n", + "* Xarray essentials\n", + "* Xarray Variable\n", + "## Plot the data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "import random\n", + "\n", + "import glob\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "2372d5be", + "metadata": {}, + "source": [ + "## Access the data" + ] + }, + { + "cell_type": "markdown", + "id": "5b0d9684", + "metadata": {}, + "source": [ + "### How to retrieve the data\n", + "We assume the path \"/data/archive\" is available where you are running this notebook. You can use `os.path.exists(\"/data/archive\")` to verify if the path exists at your machine.\n", + "\n", + "\n", + "### Data path and file name conventions\n", + "There several common terminologies regarding ARM data, for example, data-stream-name, data-level, site, facility, instrument, etc. (For more details, please see [ARM Data File Standards Version 1.2](https://www.google.com/search?q=arm+datastream+facility+cite+definition&rlz=1C1GCEJ_enUS1029US1029&ei=hb41ZICrBPukqtsPvMODIA&ved=0ahUKEwjAgeiS0aL-AhV7kmoFHbzhAAQQ4dUDCBA&uact=5&oq=arm+datastream+facility+cite+definition&gs_lcp=Cgxnd3Mtd2l6LXNlcnAQAzIFCAAQogQyBQgAEKIEMgUIABCiBDIFCAAQogQyBQgAEKIEOgoIABBHENYEELADOgoIIRCgARDDBBAKSgQIQRgAUJ4DWIoOYO0PaAFwAXgAgAGMAYgBuQmSAQMyLjmYAQCgAQHIAQjAAQE&sclient=gws-wiz-serp).) \n", + "\n", + "For example, this notebook is called `180varanaecmwf.c1`, where `180varanaecmwf` is the \"datastream name\", and the `{process.ds_class_level}}` is the \"data level\".\n", + "\n", + "This datastream also contains site `awr` and facility `M1`. (Note: individual datastream might have multiple site-facility pairs.)\n", + "In such a case, the data of this data-stream is stored at `/data/archive/awr/awr180varanaecmwfM1.c1`, which is in the format of `//.`. We can use the following method to assign the data-stream directory `datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )`\n", + "\n", + "The data files under datastream_dir also follows naming conventions. But once reach the datastream_dir level, the most import file naming convention to differentiate the files is \"yyyyMMdd.hhmmss\", which comes handy to filter out files based on datetime. For example, we can use `glob.glob(f'{datastream_dir}/*.200709*.*')` to filter files in 2007 September.\n", + "\n", + "(Note: refer to the https://adc.arm.gov/discovery/#/ and https://adc.arm.gov/solr8/metadata/select API to explore ARM datastream and assoicated available sites and facilities\n", + "\n", + "Please see the following examples in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d7e9eb85", + "metadata": {}, + "outputs": [], + "source": [ + "# Verify if DATA_DIR path exists\n", + "DATA_DIR = \"/data/archive\"\n", + "os.path.exists(DATA_DIR)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "586993fd", + "metadata": {}, + "outputs": [], + "source": [ + "# Speicify datastream_dir following the path conventions and check its existence\n", + "DATASTREAM_NAME = \"180varanaecmwf\"\n", + "DATA_LEVEL = \"c1\"\n", + "site = \"awr\"\n", + "facility = \"M1\"\n", + "datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )\n", + "print(datastream_dir)\n", + "print(os.path.exists(datastream_dir))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0742f7c1", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: list 5 (random) files under datastream_dir\n", + "files = os.listdir(datastream_dir)\n", + "files[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39b98a36", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: get most recent file\n", + "list_of_files = glob.glob(f\"{datastream_dir}/*\") # * means all if need specific format then *.csv\n", + "latest_file = max(list_of_files, key=os.path.getctime)\n", + "latest_file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "902d514e", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus sort datastream files based on datetime\n", + "files = os.listdir(datastream_dir)\n", + "file_sorted = files.copy()\n", + "file_sorted.sort() \n", + "print(file_sorted[:5])\n", + "\n", + "# to reverse\n", + "file_sorted_reverse = files.copy() \n", + "file_sorted_reverse.sort(reverse=True)\n", + "print(file_sorted_reverse[:5])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec5923b2", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: pattern matching\n", + "# filter the 200709** files under datastream_dir\n", + "files_filter = glob.glob(f'{datastream_dir}/*.200709*.*')\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c4a4aa18", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: use armnotebook_utils.file_filter (TODO) to filter files based on datastream info \n" + ] + }, + { + "cell_type": "markdown", + "id": "cfeb9efc", + "metadata": {}, + "source": [ + "### Load data\n", + "The arm data is stored in [NetCDF](https://en.wikipedia.org/wiki/NetCDF#:~:text=%22NetCDF%20(network%20Common%20Data%20Form,format%20for%20representing%20scientific%20data.) format. We can use xarray's `open_dataset` method to load single file, or `open_mfdataset` to open multiple files. (Note: the latter will still return a single xarray dataset object by combining multiple files.)\n", + "\n", + "Note: open_dataset keeps the file handle open and lazy loads its contents. All parameters are passed directly to open_dataset. It is a preferable method over load_dataset for memory-effiency. (more details, please see [xarray.open_dataset](https://docs.xarray.dev/en/stable/generated/xarray.open_dataset.html))\n", + "\n", + "See the following example in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a440a329", + "metadata": {}, + "outputs": [], + "source": [ + "# open single file\n", + "full_path = latest_file\n", + "print(full_path)\n", + "ds_single = xr.open_dataset(full_path)\n", + "ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0143a3d", + "metadata": {}, + "outputs": [], + "source": [ + "print(ds_single)\n", + "print(type(ds_single))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c0f8939", + "metadata": {}, + "outputs": [], + "source": [ + "# open multiple files\n", + "n_files = 3\n", + "full_paths = [os.path.join(datastream_dir, f_path) for f_path in file_sorted_reverse[:n_files]]\n", + "print(full_paths)\n", + "try: # Note: sometimes multiple files cannot be merged, so we used try except here.\n", + " ds_mutiple = xr.open_mfdataset(full_paths)\n", + " # ds_mutiple\n", + " print(type(ds_mutiple))\n", + " print(ds_mutiple)\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "3fa59943", + "metadata": {}, + "source": [ + "## Explore the data" + ] + }, + { + "cell_type": "markdown", + "id": "571f69b9", + "metadata": {}, + "source": [ + "### NetCDF Data structure \n", + "\n", + "\n", + "(If you are confident with NetCDF basics and xarray essentials, feel free to skip this session.)\n", + "Before we dive into data exploration, there are some eseential concpets we should be familiar with (shown below): \n", + "* Dataset\n", + "* Data array\n", + "* Variable\n", + "* Dimenssion\n", + "* Coordinate\n", + "* Data Type\n", + "* Meta Data (Attributes)\n", + "\n", + "We will not go into details about NetCDF basics and here are some references you might find helpful\n", + "* [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#)\n", + "* [Network Common Data Form (NetCDF)](https://www.unidata.ucar.edu/software/netcdf/) and [A Brief History of (netCDF) Time](https://www.unidata.ucar.edu/software/netcdf/time/recs.html)\n", + "\n", + "\n", + "\n", + "### Xarray essentials\n", + "Earlier we introduced the xarray pacakge and used `open_dataset` and `open_mfdataset` to retrieve NetCDF data file as an xarray Dataset object. Recall that you can review the data in a notebook by using `print(ds)` or `ds`. \n", + "\n", + "Using xarray to retrieve the aforementioned NetCDF basics is straightforwared. In general,\n", + "* Dataset: `ds`\n", + "* Data array: `ds.variables`\n", + "* Variable: `ds.ds.variables`\n", + "* Dimenssion: `ds.dims`\n", + "* Coordinate: `ds.coords`\n", + "* Data Type: `type`\n", + "* Meta Data (Attributes)\n", + "\n", + "Also, here are some references if you are new to xarray\n", + "* [Xarray in 45 minutes](https://tutorial.xarray.dev/overview/xarray-in-45-min.html)\n", + "* [Handling NetCDF Files using XArray for Absolute Beginners](https://towardsdatascience.com/handling-netcdf-files-using-xarray-for-absolute-beginners-111a8ab4463f)\n", + "\n", + "Try the following commands in action\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b3eac323", + "metadata": {}, + "outputs": [], + "source": [ + "ds = ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77ecf85d", + "metadata": {}, + "outputs": [], + "source": [ + "# Dataset \n", + "ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25e7de09", + "metadata": {}, + "outputs": [], + "source": [ + "# Data array, Variable\n", + "# Note: the info can be overwhelming. Do not attempt to grasp everything at the first glance.\n", + "ds.variables" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c41b67e", + "metadata": {}, + "outputs": [], + "source": [ + "# dimenssions\n", + "ds.dims" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "156f1dfc", + "metadata": {}, + "outputs": [], + "source": [ + "# coordinates\n", + "ds.coords" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "277d6064", + "metadata": {}, + "outputs": [], + "source": [ + "# Meta Data (Attributes)\n", + "ds.attrs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d334681f", + "metadata": {}, + "outputs": [], + "source": [ + "# type\n", + "print(type(ds))\n", + "print(type(ds.variables))\n", + "print(type(ds.dims))\n", + "print(type(ds.coords))\n", + "print(type(ds.attrs))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "643399d6", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: retrieve variable names only\n", + "list(ds.variables)" + ] + }, + { + "cell_type": "markdown", + "id": "1cf36878", + "metadata": {}, + "source": [ + "#### Discussion: variable vs. coordinates vs. dimenssions. \n", + "From [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#):\n", + "\n", + "* Dimession: A dimension may be used to represent a real physical dimension, for example, time, latitude, longitude, or height. A dimension might also be used to index other quantities, for example station or model-run-number.\n", + "A netCDF dimension has both a name and a length. A dimension length is an arbitrary positive integer, except that one dimension in a netCDF dataset can have the length UNLIMITED.\n", + "\n", + "* Variables: Variables are used to store the bulk of the data in a netCDF dataset. A variable represents an array of values of the same type. A scalar value is treated as a 0-dimensional array. A variable has a name, a data type, and a shape described by its list of dimensions specified when the variable is created. A variable may also have associated attributes, which may be added, deleted or changed after the variable is created.\n", + "\n", + "* Coordinate (Variables): It is legal for a variable to have the same name as a dimension. Such variables have no special meaning to the netCDF library. However there is a convention that such variables should be treated in a special way by software using this library.\n", + "A variable with the same name as a dimension is called a coordinate variable. It typically defines a physical coordinate corresponding to that dimension.\n", + "\n", + "\n", + "Tips: By definition, coordinates and dimenssions are also variable. For example ds.time is a coordinate but it is also a special variable. But in practice, when we talk about variable, it implies regular/non-coordinate variable. We will use this convention for the remaining notebook. " + ] + }, + { + "cell_type": "markdown", + "id": "d47120f9", + "metadata": {}, + "source": [ + "### Xarray Variable\n", + "\n", + "\n", + "We can use `ds.variables` to access varialbes of a dataset. But many times viewing print-out the whole datasets can be overwhemling. Instead, we would work on the individual variable (also called Xarray data array.) using the `ds[var_name]` syntax, where `var_name` is the name of the variable. See the following examples in action.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5a57e136", + "metadata": {}, + "outputs": [], + "source": [ + "print(type(ds[\"time\"]))\n", + "ds[\"time\"]" + ] + }, + { + "cell_type": "markdown", + "id": "51ec643e", + "metadata": {}, + "source": [ + "#### Variable properties\n", + "For individual varible, the following properties are mostly used with the assocaited xarray method to retrieve them. (assuming var = ds[var_name])\n", + "* name: `var.name`\n", + "* data content: `var.data`\n", + "* attributes: `var.attrs`\n", + "* dimenstions: `var.dims`\n", + "* data type: `var.data.dtype`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a632a525", + "metadata": {}, + "outputs": [], + "source": [ + "var_name = \"time\"\n", + "var = ds[var_name]\n", + "\n", + "print(\"var.name: \\n\", var.name, \"\\n\")\n", + "print(\"var.data: \\n\", var.data, \"\\n\")\n", + "print(\"var.attrs: \\n\", var.attrs, \"\\n\")\n", + "print(\"var.dims: \\n\", var.dims, \"\\n\")\n", + "print(\"var.data.dtype: \\n\", var.data.dtype, \"\\n\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b256b07f", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: indivdual variable info table (collect and display variable info as a tabular format)\n", + "df_info = pd.DataFrame()\n", + "df_info[\"var_name\"] = list(ds.variables)\n", + "df_info[\"dims\"] = df_info.var_name.apply(lambda x: ds[x].dims)\n", + "df_info[\"is_dim\"] = df_info.var_name.apply(lambda x: x in ds.dims)\n", + "df_info[\"n_dim\"] = df_info.var_name.apply(lambda x: len(ds[x].dims))\n", + "df_info[\"attrs\"] = df_info.var_name.apply(lambda x: ds[x].attrs)\n", + "df_info[\"dtype\"] = df_info.var_name.apply(lambda x: ds[x].data.dtype)\n", + "df_info" + ] + }, + { + "cell_type": "markdown", + "id": "e6a36b96", + "metadata": {}, + "source": [ + "### Data cleaning/Preprocessing (skipped)\n", + "Data cleaning and preprocessing is an important stage in the the data analysis pipeline. However it will be out of the scope of this notebook. For more information about data cleaning and preprocessing basics, here are some references. \n", + "* [Xarray Fundamentals](https://earth-env-data-science.github.io/lectures/xarray/xarray.html)\n", + "* [Xarray Tutorial — Pangeo Gallery documentation](http://gallery.pangeo.io/repos/pangeo-data/pangeo-tutorial-gallery/xarray.html)\n", + "* [Pythonic Data Cleaning With pandas and NumPy](https://realpython.com/python-data-cleaning-numpy-pandas/)\n", + "* [Pandas - Cleaning Data](https://www.w3schools.com/python/pandas/pandas_cleaning.asp)" + ] + }, + { + "cell_type": "markdown", + "id": "ae6e1a69", + "metadata": {}, + "source": [ + "### Plotting\n", + "Data visualization (or plotting) is another important data analysis topic and deserves its own discussion. Here in this notebook we will only demonstrate basic tool for simple data visualization tasks.\n", + "\n", + "Here are some reference you might find useful:\n", + "* xarray plotting: https://docs.xarray.dev/en/stable/user-guide/plotting.html\n", + "* Atmospheric Community Toolkit (ACT): https://arm-doe.github.io/ACT/index.html\n", + "\n", + "Note: this notebook is auto-generated using a template. It uses a general idea to select variable(s) to plot and is not customized to each indivdual notebook. Feel free to change the variables in intrrests, especially certain figure is failed to plot." + ] + }, + { + "cell_type": "markdown", + "id": "7e417fb2", + "metadata": {}, + "source": [ + "#### 1-dimenssional basic time series plot\n", + "\n", + "For the following plot we would like to find variables such that\n", + "* it has one and only one dimession\n", + "* \"time\" is its coordinate variable\n", + "* it is not a dimenssion itself,\n", + "* it is not a special variable with substrings within [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "\n", + "Please see the following example in action. (For more details about pandas filtering, please see the following references.)\n", + "* [pandas: multiple conditions while indexing data frame](https://stackoverflow.com/questions/22591174/pandas-multiple-conditions-while-indexing-data-frame-unexpected-behavior)\n", + "* [pandas.DataFrame.apply](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.apply.html)\n", + "* [How to test if a string contains one of the substrings in a list...](https://stackoverflow.com/questions/26577516/how-to-test-if-a-string-contains-one-of-the-substrings-in-a-list-in-pandas" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30edac2d", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==1, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter = df_info[(df_info.n_dim == 1) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4777a659", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_1d = df_filter.var_name.values[0]\n", + " var_1d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb5ae985", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " ds[var_1d].plot()\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "9e41066e", + "metadata": {}, + "source": [ + "#### 2-dimenssional basic plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09bd4adc", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==2, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_2 = df_info[(df_info.n_dim == 2) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2cdea1a9", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_2d = df_filter_2.var_name.values[0]\n", + " var_2d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5f48879", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " print(ds[var_2d].dims)\n", + " # ds[var_2d].plot()\n", + "\n", + " # conventionally, use \"time\" as x-axis\n", + " ds[var_2d].plot(x=\"time\")\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "7554b2a6", + "metadata": {}, + "source": [ + "#### qc-plotting (optional)\n", + "\n", + "Note: act qc plotting has more strict requirement, one of them is the associated qc variable need to have \"flag_masks\" attributes. Which is added by using ds = act.io.armfiles.read_netcdf(files_list), then ds.clean.cleanup()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71c2096f", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " ds_act = act.io.armfiles.read_netcdf(full_path)\n", + " print(type(ds_act))\n", + " ds_act.clean.cleanup()\n", + "\n", + " # or \n", + " # ds.clean.cleanup()\n", + "except Exception as e:\n", + " print(\"ERROR\", e)\n", + " ds_act = ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "844f8505", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter valid variables for ACT qc plotting\n", + "condition = (\"qc_\" + df_info.var_name).apply(lambda x: ds_act[x].attrs.get(\"flag_masks\") is not None \n", + " if x in list(ds.variables) else False)\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_3 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings))) &\n", + " condition\n", + " ]\n", + "df_filter_3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd38cc6", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "try:\n", + " qc_variable = df_filter_3.var_name.values[0]\n", + " print(qc_variable)\n", + "except Exception as e:\n", + " print(e)\n", + " \n", + "try:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + " qc_ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "d76e4d27", + "metadata": {}, + "source": [ + "#### bonus: choose variables to plot from a dropdown menu " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb733804", + "metadata": {}, + "outputs": [], + "source": [ + "# Valid variables filtering\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_4 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82d5c20d", + "metadata": {}, + "outputs": [], + "source": [ + "# example 1: using xarray plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2fc9aaa4", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# fig, ax = plt.subplots(figsize=(10, 4))\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "# fig.clear() # Remove old lines from plot and plot new one\n", + "# if len(ds[var].dims)==2:\n", + "# ds[var].plot(x=\"time\", add_colorbar=False)\n", + "# else:\n", + "# ds[var].plot()\n", + "# plt.grid()\n", + "# plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fabdd802", + "metadata": {}, + "outputs": [], + "source": [ + "# example 2: using act plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "962b4186", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "\n", + "\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "\n", + "# i_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + "# i_display.add_subplots((1,), figsize=(10, 4))\n", + "# ax = i_display.plot(var, subplot_index=(0,), set_title=f\"{var} ({ds_act[var].attrs['long_name']})\",)\n", + "\n", + "# ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "# ax.grid()\n", + "# plt.show()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/VARANAL3D/180varanal3dera5.c1.ipynb b/VAPs/quicklook/VARANAL3D/180varanal3dera5.c1.ipynb new file mode 100644 index 00000000..662c5dee --- /dev/null +++ b/VAPs/quicklook/VARANAL3D/180varanal3dera5.c1.ipynb @@ -0,0 +1,297 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# 180VARANAL3DERA5.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/varanal3d) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = '180varanal3dera5'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2011-06-06', 'facility': 'C1', 'site': 'sgp', 'start_date': '2011-04-22'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2011-04-20'\n", + "date_end = '2011-04-22'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['u', 'v', 'T']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'u'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/VARANAL3D/180varanal3dncep.c1.ipynb b/VAPs/quicklook/VARANAL3D/180varanal3dncep.c1.ipynb new file mode 100644 index 00000000..8879ec8a --- /dev/null +++ b/VAPs/quicklook/VARANAL3D/180varanal3dncep.c1.ipynb @@ -0,0 +1,297 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# 180VARANAL3DNCEP.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/varanal3d) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = '180varanal3dncep'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2011-06-06', 'facility': 'C1', 'site': 'sgp', 'start_date': '2011-04-22'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2011-04-20'\n", + "date_end = '2011-04-22'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['u', 'v', 'T']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'u'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/VARANAL3D/VARANAL3D_tutorial.ipynb b/VAPs/quicklook/VARANAL3D/VARANAL3D_tutorial.ipynb new file mode 100644 index 00000000..5dfa64cf --- /dev/null +++ b/VAPs/quicklook/VARANAL3D/VARANAL3D_tutorial.ipynb @@ -0,0 +1,838 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# 180VARANAL3DERA5.C1 Notebook\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/varanal3d) for more information about this vap." + ] + }, + { + "cell_type": "markdown", + "id": "97097763", + "metadata": {}, + "source": [ + "In this notebook, we demonstrate the workflow to explore ARM vap data (using 180varanal3dera5 as an example.) Value-added products (VAPs) are higher-order data products that have been analyzed and processed to ease scientists’ use of ARM data in atmospheric research and global climate models. \n", + "Here is the main content we will cover." + ] + }, + { + "cell_type": "markdown", + "id": "eddec40f", + "metadata": {}, + "source": [ + "# Table of Content\n", + "## Access the data\n", + "* How to retrieve the data\n", + "* Data path and file name conventions\n", + "* Load data\n", + "## Explore the data\n", + "* NetCDF Data structure\n", + "* Xarray essentials\n", + "* Xarray Variable\n", + "## Plot the data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "import random\n", + "\n", + "import glob\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "2372d5be", + "metadata": {}, + "source": [ + "## Access the data" + ] + }, + { + "cell_type": "markdown", + "id": "5b0d9684", + "metadata": {}, + "source": [ + "### How to retrieve the data\n", + "We assume the path \"/data/archive\" is available where you are running this notebook. You can use `os.path.exists(\"/data/archive\")` to verify if the path exists at your machine.\n", + "\n", + "\n", + "### Data path and file name conventions\n", + "There several common terminologies regarding ARM data, for example, data-stream-name, data-level, site, facility, instrument, etc. (For more details, please see [ARM Data File Standards Version 1.2](https://www.google.com/search?q=arm+datastream+facility+cite+definition&rlz=1C1GCEJ_enUS1029US1029&ei=hb41ZICrBPukqtsPvMODIA&ved=0ahUKEwjAgeiS0aL-AhV7kmoFHbzhAAQQ4dUDCBA&uact=5&oq=arm+datastream+facility+cite+definition&gs_lcp=Cgxnd3Mtd2l6LXNlcnAQAzIFCAAQogQyBQgAEKIEMgUIABCiBDIFCAAQogQyBQgAEKIEOgoIABBHENYEELADOgoIIRCgARDDBBAKSgQIQRgAUJ4DWIoOYO0PaAFwAXgAgAGMAYgBuQmSAQMyLjmYAQCgAQHIAQjAAQE&sclient=gws-wiz-serp).) \n", + "\n", + "For example, this notebook is called `180varanal3dera5.c1`, where `180varanal3dera5` is the \"datastream name\", and the `{process.ds_class_level}}` is the \"data level\".\n", + "\n", + "This datastream also contains site `sgp` and facility `C1`. (Note: individual datastream might have multiple site-facility pairs.)\n", + "In such a case, the data of this data-stream is stored at `/data/archive/sgp/sgp180varanal3dera5C1.c1`, which is in the format of `//.`. We can use the following method to assign the data-stream directory `datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )`\n", + "\n", + "The data files under datastream_dir also follows naming conventions. But once reach the datastream_dir level, the most import file naming convention to differentiate the files is \"yyyyMMdd.hhmmss\", which comes handy to filter out files based on datetime. For example, we can use `glob.glob(f'{datastream_dir}/*.200709*.*')` to filter files in 2007 September.\n", + "\n", + "(Note: refer to the https://adc.arm.gov/discovery/#/ and https://adc.arm.gov/solr8/metadata/select API to explore ARM datastream and assoicated available sites and facilities\n", + "\n", + "Please see the following examples in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d7e9eb85", + "metadata": {}, + "outputs": [], + "source": [ + "# Verify if DATA_DIR path exists\n", + "DATA_DIR = \"/data/archive\"\n", + "os.path.exists(DATA_DIR)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "586993fd", + "metadata": {}, + "outputs": [], + "source": [ + "# Speicify datastream_dir following the path conventions and check its existence\n", + "DATASTREAM_NAME = \"180varanal3dera5\"\n", + "DATA_LEVEL = \"c1\"\n", + "site = \"sgp\"\n", + "facility = \"C1\"\n", + "datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )\n", + "print(datastream_dir)\n", + "print(os.path.exists(datastream_dir))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0742f7c1", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: list 5 (random) files under datastream_dir\n", + "files = os.listdir(datastream_dir)\n", + "files[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39b98a36", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: get most recent file\n", + "list_of_files = glob.glob(f\"{datastream_dir}/*\") # * means all if need specific format then *.csv\n", + "latest_file = max(list_of_files, key=os.path.getctime)\n", + "latest_file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "902d514e", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus sort datastream files based on datetime\n", + "files = os.listdir(datastream_dir)\n", + "file_sorted = files.copy()\n", + "file_sorted.sort() \n", + "print(file_sorted[:5])\n", + "\n", + "# to reverse\n", + "file_sorted_reverse = files.copy() \n", + "file_sorted_reverse.sort(reverse=True)\n", + "print(file_sorted_reverse[:5])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec5923b2", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: pattern matching\n", + "# filter the 200709** files under datastream_dir\n", + "files_filter = glob.glob(f'{datastream_dir}/*.200709*.*')\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c4a4aa18", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: use armnotebook_utils.file_filter (TODO) to filter files based on datastream info \n" + ] + }, + { + "cell_type": "markdown", + "id": "cfeb9efc", + "metadata": {}, + "source": [ + "### Load data\n", + "The arm data is stored in [NetCDF](https://en.wikipedia.org/wiki/NetCDF#:~:text=%22NetCDF%20(network%20Common%20Data%20Form,format%20for%20representing%20scientific%20data.) format. We can use xarray's `open_dataset` method to load single file, or `open_mfdataset` to open multiple files. (Note: the latter will still return a single xarray dataset object by combining multiple files.)\n", + "\n", + "Note: open_dataset keeps the file handle open and lazy loads its contents. All parameters are passed directly to open_dataset. It is a preferable method over load_dataset for memory-effiency. (more details, please see [xarray.open_dataset](https://docs.xarray.dev/en/stable/generated/xarray.open_dataset.html))\n", + "\n", + "See the following example in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a440a329", + "metadata": {}, + "outputs": [], + "source": [ + "# open single file\n", + "full_path = latest_file\n", + "print(full_path)\n", + "ds_single = xr.open_dataset(full_path)\n", + "ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0143a3d", + "metadata": {}, + "outputs": [], + "source": [ + "print(ds_single)\n", + "print(type(ds_single))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c0f8939", + "metadata": {}, + "outputs": [], + "source": [ + "# open multiple files\n", + "n_files = 3\n", + "full_paths = [os.path.join(datastream_dir, f_path) for f_path in file_sorted_reverse[:n_files]]\n", + "print(full_paths)\n", + "try: # Note: sometimes multiple files cannot be merged, so we used try except here.\n", + " ds_mutiple = xr.open_mfdataset(full_paths)\n", + " # ds_mutiple\n", + " print(type(ds_mutiple))\n", + " print(ds_mutiple)\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "3fa59943", + "metadata": {}, + "source": [ + "## Explore the data" + ] + }, + { + "cell_type": "markdown", + "id": "571f69b9", + "metadata": {}, + "source": [ + "### NetCDF Data structure \n", + "\n", + "\n", + "(If you are confident with NetCDF basics and xarray essentials, feel free to skip this session.)\n", + "Before we dive into data exploration, there are some eseential concpets we should be familiar with (shown below): \n", + "* Dataset\n", + "* Data array\n", + "* Variable\n", + "* Dimenssion\n", + "* Coordinate\n", + "* Data Type\n", + "* Meta Data (Attributes)\n", + "\n", + "We will not go into details about NetCDF basics and here are some references you might find helpful\n", + "* [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#)\n", + "* [Network Common Data Form (NetCDF)](https://www.unidata.ucar.edu/software/netcdf/) and [A Brief History of (netCDF) Time](https://www.unidata.ucar.edu/software/netcdf/time/recs.html)\n", + "\n", + "\n", + "\n", + "### Xarray essentials\n", + "Earlier we introduced the xarray pacakge and used `open_dataset` and `open_mfdataset` to retrieve NetCDF data file as an xarray Dataset object. Recall that you can review the data in a notebook by using `print(ds)` or `ds`. \n", + "\n", + "Using xarray to retrieve the aforementioned NetCDF basics is straightforwared. In general,\n", + "* Dataset: `ds`\n", + "* Data array: `ds.variables`\n", + "* Variable: `ds.ds.variables`\n", + "* Dimenssion: `ds.dims`\n", + "* Coordinate: `ds.coords`\n", + "* Data Type: `type`\n", + "* Meta Data (Attributes)\n", + "\n", + "Also, here are some references if you are new to xarray\n", + "* [Xarray in 45 minutes](https://tutorial.xarray.dev/overview/xarray-in-45-min.html)\n", + "* [Handling NetCDF Files using XArray for Absolute Beginners](https://towardsdatascience.com/handling-netcdf-files-using-xarray-for-absolute-beginners-111a8ab4463f)\n", + "\n", + "Try the following commands in action\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b3eac323", + "metadata": {}, + "outputs": [], + "source": [ + "ds = ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77ecf85d", + "metadata": {}, + "outputs": [], + "source": [ + "# Dataset \n", + "ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25e7de09", + "metadata": {}, + "outputs": [], + "source": [ + "# Data array, Variable\n", + "# Note: the info can be overwhelming. Do not attempt to grasp everything at the first glance.\n", + "ds.variables" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c41b67e", + "metadata": {}, + "outputs": [], + "source": [ + "# dimenssions\n", + "ds.dims" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "156f1dfc", + "metadata": {}, + "outputs": [], + "source": [ + "# coordinates\n", + "ds.coords" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "277d6064", + "metadata": {}, + "outputs": [], + "source": [ + "# Meta Data (Attributes)\n", + "ds.attrs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d334681f", + "metadata": {}, + "outputs": [], + "source": [ + "# type\n", + "print(type(ds))\n", + "print(type(ds.variables))\n", + "print(type(ds.dims))\n", + "print(type(ds.coords))\n", + "print(type(ds.attrs))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "643399d6", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: retrieve variable names only\n", + "list(ds.variables)" + ] + }, + { + "cell_type": "markdown", + "id": "1cf36878", + "metadata": {}, + "source": [ + "#### Discussion: variable vs. coordinates vs. dimenssions. \n", + "From [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#):\n", + "\n", + "* Dimession: A dimension may be used to represent a real physical dimension, for example, time, latitude, longitude, or height. A dimension might also be used to index other quantities, for example station or model-run-number.\n", + "A netCDF dimension has both a name and a length. A dimension length is an arbitrary positive integer, except that one dimension in a netCDF dataset can have the length UNLIMITED.\n", + "\n", + "* Variables: Variables are used to store the bulk of the data in a netCDF dataset. A variable represents an array of values of the same type. A scalar value is treated as a 0-dimensional array. A variable has a name, a data type, and a shape described by its list of dimensions specified when the variable is created. A variable may also have associated attributes, which may be added, deleted or changed after the variable is created.\n", + "\n", + "* Coordinate (Variables): It is legal for a variable to have the same name as a dimension. Such variables have no special meaning to the netCDF library. However there is a convention that such variables should be treated in a special way by software using this library.\n", + "A variable with the same name as a dimension is called a coordinate variable. It typically defines a physical coordinate corresponding to that dimension.\n", + "\n", + "\n", + "Tips: By definition, coordinates and dimenssions are also variable. For example ds.time is a coordinate but it is also a special variable. But in practice, when we talk about variable, it implies regular/non-coordinate variable. We will use this convention for the remaining notebook. " + ] + }, + { + "cell_type": "markdown", + "id": "d47120f9", + "metadata": {}, + "source": [ + "### Xarray Variable\n", + "\n", + "\n", + "We can use `ds.variables` to access varialbes of a dataset. But many times viewing print-out the whole datasets can be overwhemling. Instead, we would work on the individual variable (also called Xarray data array.) using the `ds[var_name]` syntax, where `var_name` is the name of the variable. See the following examples in action.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5a57e136", + "metadata": {}, + "outputs": [], + "source": [ + "print(type(ds[\"time\"]))\n", + "ds[\"time\"]" + ] + }, + { + "cell_type": "markdown", + "id": "51ec643e", + "metadata": {}, + "source": [ + "#### Variable properties\n", + "For individual varible, the following properties are mostly used with the assocaited xarray method to retrieve them. (assuming var = ds[var_name])\n", + "* name: `var.name`\n", + "* data content: `var.data`\n", + "* attributes: `var.attrs`\n", + "* dimenstions: `var.dims`\n", + "* data type: `var.data.dtype`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a632a525", + "metadata": {}, + "outputs": [], + "source": [ + "var_name = \"time\"\n", + "var = ds[var_name]\n", + "\n", + "print(\"var.name: \\n\", var.name, \"\\n\")\n", + "print(\"var.data: \\n\", var.data, \"\\n\")\n", + "print(\"var.attrs: \\n\", var.attrs, \"\\n\")\n", + "print(\"var.dims: \\n\", var.dims, \"\\n\")\n", + "print(\"var.data.dtype: \\n\", var.data.dtype, \"\\n\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b256b07f", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: indivdual variable info table (collect and display variable info as a tabular format)\n", + "df_info = pd.DataFrame()\n", + "df_info[\"var_name\"] = list(ds.variables)\n", + "df_info[\"dims\"] = df_info.var_name.apply(lambda x: ds[x].dims)\n", + "df_info[\"is_dim\"] = df_info.var_name.apply(lambda x: x in ds.dims)\n", + "df_info[\"n_dim\"] = df_info.var_name.apply(lambda x: len(ds[x].dims))\n", + "df_info[\"attrs\"] = df_info.var_name.apply(lambda x: ds[x].attrs)\n", + "df_info[\"dtype\"] = df_info.var_name.apply(lambda x: ds[x].data.dtype)\n", + "df_info" + ] + }, + { + "cell_type": "markdown", + "id": "e6a36b96", + "metadata": {}, + "source": [ + "### Data cleaning/Preprocessing (skipped)\n", + "Data cleaning and preprocessing is an important stage in the the data analysis pipeline. However it will be out of the scope of this notebook. For more information about data cleaning and preprocessing basics, here are some references. \n", + "* [Xarray Fundamentals](https://earth-env-data-science.github.io/lectures/xarray/xarray.html)\n", + "* [Xarray Tutorial — Pangeo Gallery documentation](http://gallery.pangeo.io/repos/pangeo-data/pangeo-tutorial-gallery/xarray.html)\n", + "* [Pythonic Data Cleaning With pandas and NumPy](https://realpython.com/python-data-cleaning-numpy-pandas/)\n", + "* [Pandas - Cleaning Data](https://www.w3schools.com/python/pandas/pandas_cleaning.asp)" + ] + }, + { + "cell_type": "markdown", + "id": "ae6e1a69", + "metadata": {}, + "source": [ + "### Plotting\n", + "Data visualization (or plotting) is another important data analysis topic and deserves its own discussion. Here in this notebook we will only demonstrate basic tool for simple data visualization tasks.\n", + "\n", + "Here are some reference you might find useful:\n", + "* xarray plotting: https://docs.xarray.dev/en/stable/user-guide/plotting.html\n", + "* Atmospheric Community Toolkit (ACT): https://arm-doe.github.io/ACT/index.html\n", + "\n", + "Note: this notebook is auto-generated using a template. It uses a general idea to select variable(s) to plot and is not customized to each indivdual notebook. Feel free to change the variables in intrrests, especially certain figure is failed to plot." + ] + }, + { + "cell_type": "markdown", + "id": "7e417fb2", + "metadata": {}, + "source": [ + "#### 1-dimenssional basic time series plot\n", + "\n", + "For the following plot we would like to find variables such that\n", + "* it has one and only one dimession\n", + "* \"time\" is its coordinate variable\n", + "* it is not a dimenssion itself,\n", + "* it is not a special variable with substrings within [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "\n", + "Please see the following example in action. (For more details about pandas filtering, please see the following references.)\n", + "* [pandas: multiple conditions while indexing data frame](https://stackoverflow.com/questions/22591174/pandas-multiple-conditions-while-indexing-data-frame-unexpected-behavior)\n", + "* [pandas.DataFrame.apply](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.apply.html)\n", + "* [How to test if a string contains one of the substrings in a list...](https://stackoverflow.com/questions/26577516/how-to-test-if-a-string-contains-one-of-the-substrings-in-a-list-in-pandas" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30edac2d", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==1, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter = df_info[(df_info.n_dim == 1) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4777a659", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_1d = df_filter.var_name.values[0]\n", + " var_1d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb5ae985", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " ds[var_1d].plot()\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "9e41066e", + "metadata": {}, + "source": [ + "#### 2-dimenssional basic plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09bd4adc", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==2, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_2 = df_info[(df_info.n_dim == 2) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2cdea1a9", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_2d = df_filter_2.var_name.values[0]\n", + " var_2d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5f48879", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " print(ds[var_2d].dims)\n", + " # ds[var_2d].plot()\n", + "\n", + " # conventionally, use \"time\" as x-axis\n", + " ds[var_2d].plot(x=\"time\")\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "7554b2a6", + "metadata": {}, + "source": [ + "#### qc-plotting (optional)\n", + "\n", + "Note: act qc plotting has more strict requirement, one of them is the associated qc variable need to have \"flag_masks\" attributes. Which is added by using ds = act.io.armfiles.read_netcdf(files_list), then ds.clean.cleanup()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71c2096f", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " ds_act = act.io.armfiles.read_netcdf(full_path)\n", + " print(type(ds_act))\n", + " ds_act.clean.cleanup()\n", + "\n", + " # or \n", + " # ds.clean.cleanup()\n", + "except Exception as e:\n", + " print(\"ERROR\", e)\n", + " ds_act = ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "844f8505", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter valid variables for ACT qc plotting\n", + "condition = (\"qc_\" + df_info.var_name).apply(lambda x: ds_act[x].attrs.get(\"flag_masks\") is not None \n", + " if x in list(ds.variables) else False)\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_3 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings))) &\n", + " condition\n", + " ]\n", + "df_filter_3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd38cc6", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "try:\n", + " qc_variable = df_filter_3.var_name.values[0]\n", + " print(qc_variable)\n", + "except Exception as e:\n", + " print(e)\n", + " \n", + "try:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + " qc_ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "d76e4d27", + "metadata": {}, + "source": [ + "#### bonus: choose variables to plot from a dropdown menu " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb733804", + "metadata": {}, + "outputs": [], + "source": [ + "# Valid variables filtering\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_4 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82d5c20d", + "metadata": {}, + "outputs": [], + "source": [ + "# example 1: using xarray plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2fc9aaa4", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# fig, ax = plt.subplots(figsize=(10, 4))\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "# fig.clear() # Remove old lines from plot and plot new one\n", + "# if len(ds[var].dims)==2:\n", + "# ds[var].plot(x=\"time\", add_colorbar=False)\n", + "# else:\n", + "# ds[var].plot()\n", + "# plt.grid()\n", + "# plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fabdd802", + "metadata": {}, + "outputs": [], + "source": [ + "# example 2: using act plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "962b4186", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "\n", + "\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "\n", + "# i_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + "# i_display.add_subplots((1,), figsize=(10, 4))\n", + "# ax = i_display.plot(var, subplot_index=(0,), set_title=f\"{var} ({ds_act[var].attrs['long_name']})\",)\n", + "\n", + "# ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "# ax.grid()\n", + "# plt.show()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/VDISQUANTS/VDISQUANTS_tutorial.ipynb b/VAPs/quicklook/VDISQUANTS/VDISQUANTS_tutorial.ipynb new file mode 100644 index 00000000..f36f368b --- /dev/null +++ b/VAPs/quicklook/VDISQUANTS/VDISQUANTS_tutorial.ipynb @@ -0,0 +1,838 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# VDISQUANTS.C1 Notebook\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/vdisquants) for more information about this vap." + ] + }, + { + "cell_type": "markdown", + "id": "97097763", + "metadata": {}, + "source": [ + "In this notebook, we demonstrate the workflow to explore ARM vap data (using vdisquants as an example.) Value-added products (VAPs) are higher-order data products that have been analyzed and processed to ease scientists’ use of ARM data in atmospheric research and global climate models. \n", + "Here is the main content we will cover." + ] + }, + { + "cell_type": "markdown", + "id": "eddec40f", + "metadata": {}, + "source": [ + "# Table of Content\n", + "## Access the data\n", + "* How to retrieve the data\n", + "* Data path and file name conventions\n", + "* Load data\n", + "## Explore the data\n", + "* NetCDF Data structure\n", + "* Xarray essentials\n", + "* Xarray Variable\n", + "## Plot the data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "import random\n", + "\n", + "import glob\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "2372d5be", + "metadata": {}, + "source": [ + "## Access the data" + ] + }, + { + "cell_type": "markdown", + "id": "5b0d9684", + "metadata": {}, + "source": [ + "### How to retrieve the data\n", + "We assume the path \"/data/archive\" is available where you are running this notebook. You can use `os.path.exists(\"/data/archive\")` to verify if the path exists at your machine.\n", + "\n", + "\n", + "### Data path and file name conventions\n", + "There several common terminologies regarding ARM data, for example, data-stream-name, data-level, site, facility, instrument, etc. (For more details, please see [ARM Data File Standards Version 1.2](https://www.google.com/search?q=arm+datastream+facility+cite+definition&rlz=1C1GCEJ_enUS1029US1029&ei=hb41ZICrBPukqtsPvMODIA&ved=0ahUKEwjAgeiS0aL-AhV7kmoFHbzhAAQQ4dUDCBA&uact=5&oq=arm+datastream+facility+cite+definition&gs_lcp=Cgxnd3Mtd2l6LXNlcnAQAzIFCAAQogQyBQgAEKIEMgUIABCiBDIFCAAQogQyBQgAEKIEOgoIABBHENYEELADOgoIIRCgARDDBBAKSgQIQRgAUJ4DWIoOYO0PaAFwAXgAgAGMAYgBuQmSAQMyLjmYAQCgAQHIAQjAAQE&sclient=gws-wiz-serp).) \n", + "\n", + "For example, this notebook is called `vdisquants.c1`, where `vdisquants` is the \"datastream name\", and the `{process.ds_class_level}}` is the \"data level\".\n", + "\n", + "This datastream also contains site `ena` and facility `C1`. (Note: individual datastream might have multiple site-facility pairs.)\n", + "In such a case, the data of this data-stream is stored at `/data/archive/ena/enavdisquantsC1.c1`, which is in the format of `//.`. We can use the following method to assign the data-stream directory `datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )`\n", + "\n", + "The data files under datastream_dir also follows naming conventions. But once reach the datastream_dir level, the most import file naming convention to differentiate the files is \"yyyyMMdd.hhmmss\", which comes handy to filter out files based on datetime. For example, we can use `glob.glob(f'{datastream_dir}/*.200709*.*')` to filter files in 2007 September.\n", + "\n", + "(Note: refer to the https://adc.arm.gov/discovery/#/ and https://adc.arm.gov/solr8/metadata/select API to explore ARM datastream and assoicated available sites and facilities\n", + "\n", + "Please see the following examples in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d7e9eb85", + "metadata": {}, + "outputs": [], + "source": [ + "# Verify if DATA_DIR path exists\n", + "DATA_DIR = \"/data/archive\"\n", + "os.path.exists(DATA_DIR)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "586993fd", + "metadata": {}, + "outputs": [], + "source": [ + "# Speicify datastream_dir following the path conventions and check its existence\n", + "DATASTREAM_NAME = \"vdisquants\"\n", + "DATA_LEVEL = \"c1\"\n", + "site = \"ena\"\n", + "facility = \"C1\"\n", + "datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )\n", + "print(datastream_dir)\n", + "print(os.path.exists(datastream_dir))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0742f7c1", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: list 5 (random) files under datastream_dir\n", + "files = os.listdir(datastream_dir)\n", + "files[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39b98a36", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: get most recent file\n", + "list_of_files = glob.glob(f\"{datastream_dir}/*\") # * means all if need specific format then *.csv\n", + "latest_file = max(list_of_files, key=os.path.getctime)\n", + "latest_file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "902d514e", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus sort datastream files based on datetime\n", + "files = os.listdir(datastream_dir)\n", + "file_sorted = files.copy()\n", + "file_sorted.sort() \n", + "print(file_sorted[:5])\n", + "\n", + "# to reverse\n", + "file_sorted_reverse = files.copy() \n", + "file_sorted_reverse.sort(reverse=True)\n", + "print(file_sorted_reverse[:5])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec5923b2", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: pattern matching\n", + "# filter the 200709** files under datastream_dir\n", + "files_filter = glob.glob(f'{datastream_dir}/*.200709*.*')\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c4a4aa18", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: use armnotebook_utils.file_filter (TODO) to filter files based on datastream info \n" + ] + }, + { + "cell_type": "markdown", + "id": "cfeb9efc", + "metadata": {}, + "source": [ + "### Load data\n", + "The arm data is stored in [NetCDF](https://en.wikipedia.org/wiki/NetCDF#:~:text=%22NetCDF%20(network%20Common%20Data%20Form,format%20for%20representing%20scientific%20data.) format. We can use xarray's `open_dataset` method to load single file, or `open_mfdataset` to open multiple files. (Note: the latter will still return a single xarray dataset object by combining multiple files.)\n", + "\n", + "Note: open_dataset keeps the file handle open and lazy loads its contents. All parameters are passed directly to open_dataset. It is a preferable method over load_dataset for memory-effiency. (more details, please see [xarray.open_dataset](https://docs.xarray.dev/en/stable/generated/xarray.open_dataset.html))\n", + "\n", + "See the following example in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a440a329", + "metadata": {}, + "outputs": [], + "source": [ + "# open single file\n", + "full_path = latest_file\n", + "print(full_path)\n", + "ds_single = xr.open_dataset(full_path)\n", + "ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0143a3d", + "metadata": {}, + "outputs": [], + "source": [ + "print(ds_single)\n", + "print(type(ds_single))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c0f8939", + "metadata": {}, + "outputs": [], + "source": [ + "# open multiple files\n", + "n_files = 3\n", + "full_paths = [os.path.join(datastream_dir, f_path) for f_path in file_sorted_reverse[:n_files]]\n", + "print(full_paths)\n", + "try: # Note: sometimes multiple files cannot be merged, so we used try except here.\n", + " ds_mutiple = xr.open_mfdataset(full_paths)\n", + " # ds_mutiple\n", + " print(type(ds_mutiple))\n", + " print(ds_mutiple)\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "3fa59943", + "metadata": {}, + "source": [ + "## Explore the data" + ] + }, + { + "cell_type": "markdown", + "id": "571f69b9", + "metadata": {}, + "source": [ + "### NetCDF Data structure \n", + "\n", + "\n", + "(If you are confident with NetCDF basics and xarray essentials, feel free to skip this session.)\n", + "Before we dive into data exploration, there are some eseential concpets we should be familiar with (shown below): \n", + "* Dataset\n", + "* Data array\n", + "* Variable\n", + "* Dimenssion\n", + "* Coordinate\n", + "* Data Type\n", + "* Meta Data (Attributes)\n", + "\n", + "We will not go into details about NetCDF basics and here are some references you might find helpful\n", + "* [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#)\n", + "* [Network Common Data Form (NetCDF)](https://www.unidata.ucar.edu/software/netcdf/) and [A Brief History of (netCDF) Time](https://www.unidata.ucar.edu/software/netcdf/time/recs.html)\n", + "\n", + "\n", + "\n", + "### Xarray essentials\n", + "Earlier we introduced the xarray pacakge and used `open_dataset` and `open_mfdataset` to retrieve NetCDF data file as an xarray Dataset object. Recall that you can review the data in a notebook by using `print(ds)` or `ds`. \n", + "\n", + "Using xarray to retrieve the aforementioned NetCDF basics is straightforwared. In general,\n", + "* Dataset: `ds`\n", + "* Data array: `ds.variables`\n", + "* Variable: `ds.ds.variables`\n", + "* Dimenssion: `ds.dims`\n", + "* Coordinate: `ds.coords`\n", + "* Data Type: `type`\n", + "* Meta Data (Attributes)\n", + "\n", + "Also, here are some references if you are new to xarray\n", + "* [Xarray in 45 minutes](https://tutorial.xarray.dev/overview/xarray-in-45-min.html)\n", + "* [Handling NetCDF Files using XArray for Absolute Beginners](https://towardsdatascience.com/handling-netcdf-files-using-xarray-for-absolute-beginners-111a8ab4463f)\n", + "\n", + "Try the following commands in action\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b3eac323", + "metadata": {}, + "outputs": [], + "source": [ + "ds = ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77ecf85d", + "metadata": {}, + "outputs": [], + "source": [ + "# Dataset \n", + "ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25e7de09", + "metadata": {}, + "outputs": [], + "source": [ + "# Data array, Variable\n", + "# Note: the info can be overwhelming. Do not attempt to grasp everything at the first glance.\n", + "ds.variables" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c41b67e", + "metadata": {}, + "outputs": [], + "source": [ + "# dimenssions\n", + "ds.dims" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "156f1dfc", + "metadata": {}, + "outputs": [], + "source": [ + "# coordinates\n", + "ds.coords" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "277d6064", + "metadata": {}, + "outputs": [], + "source": [ + "# Meta Data (Attributes)\n", + "ds.attrs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d334681f", + "metadata": {}, + "outputs": [], + "source": [ + "# type\n", + "print(type(ds))\n", + "print(type(ds.variables))\n", + "print(type(ds.dims))\n", + "print(type(ds.coords))\n", + "print(type(ds.attrs))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "643399d6", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: retrieve variable names only\n", + "list(ds.variables)" + ] + }, + { + "cell_type": "markdown", + "id": "1cf36878", + "metadata": {}, + "source": [ + "#### Discussion: variable vs. coordinates vs. dimenssions. \n", + "From [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#):\n", + "\n", + "* Dimession: A dimension may be used to represent a real physical dimension, for example, time, latitude, longitude, or height. A dimension might also be used to index other quantities, for example station or model-run-number.\n", + "A netCDF dimension has both a name and a length. A dimension length is an arbitrary positive integer, except that one dimension in a netCDF dataset can have the length UNLIMITED.\n", + "\n", + "* Variables: Variables are used to store the bulk of the data in a netCDF dataset. A variable represents an array of values of the same type. A scalar value is treated as a 0-dimensional array. A variable has a name, a data type, and a shape described by its list of dimensions specified when the variable is created. A variable may also have associated attributes, which may be added, deleted or changed after the variable is created.\n", + "\n", + "* Coordinate (Variables): It is legal for a variable to have the same name as a dimension. Such variables have no special meaning to the netCDF library. However there is a convention that such variables should be treated in a special way by software using this library.\n", + "A variable with the same name as a dimension is called a coordinate variable. It typically defines a physical coordinate corresponding to that dimension.\n", + "\n", + "\n", + "Tips: By definition, coordinates and dimenssions are also variable. For example ds.time is a coordinate but it is also a special variable. But in practice, when we talk about variable, it implies regular/non-coordinate variable. We will use this convention for the remaining notebook. " + ] + }, + { + "cell_type": "markdown", + "id": "d47120f9", + "metadata": {}, + "source": [ + "### Xarray Variable\n", + "\n", + "\n", + "We can use `ds.variables` to access varialbes of a dataset. But many times viewing print-out the whole datasets can be overwhemling. Instead, we would work on the individual variable (also called Xarray data array.) using the `ds[var_name]` syntax, where `var_name` is the name of the variable. See the following examples in action.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5a57e136", + "metadata": {}, + "outputs": [], + "source": [ + "print(type(ds[\"time\"]))\n", + "ds[\"time\"]" + ] + }, + { + "cell_type": "markdown", + "id": "51ec643e", + "metadata": {}, + "source": [ + "#### Variable properties\n", + "For individual varible, the following properties are mostly used with the assocaited xarray method to retrieve them. (assuming var = ds[var_name])\n", + "* name: `var.name`\n", + "* data content: `var.data`\n", + "* attributes: `var.attrs`\n", + "* dimenstions: `var.dims`\n", + "* data type: `var.data.dtype`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a632a525", + "metadata": {}, + "outputs": [], + "source": [ + "var_name = \"time\"\n", + "var = ds[var_name]\n", + "\n", + "print(\"var.name: \\n\", var.name, \"\\n\")\n", + "print(\"var.data: \\n\", var.data, \"\\n\")\n", + "print(\"var.attrs: \\n\", var.attrs, \"\\n\")\n", + "print(\"var.dims: \\n\", var.dims, \"\\n\")\n", + "print(\"var.data.dtype: \\n\", var.data.dtype, \"\\n\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b256b07f", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: indivdual variable info table (collect and display variable info as a tabular format)\n", + "df_info = pd.DataFrame()\n", + "df_info[\"var_name\"] = list(ds.variables)\n", + "df_info[\"dims\"] = df_info.var_name.apply(lambda x: ds[x].dims)\n", + "df_info[\"is_dim\"] = df_info.var_name.apply(lambda x: x in ds.dims)\n", + "df_info[\"n_dim\"] = df_info.var_name.apply(lambda x: len(ds[x].dims))\n", + "df_info[\"attrs\"] = df_info.var_name.apply(lambda x: ds[x].attrs)\n", + "df_info[\"dtype\"] = df_info.var_name.apply(lambda x: ds[x].data.dtype)\n", + "df_info" + ] + }, + { + "cell_type": "markdown", + "id": "e6a36b96", + "metadata": {}, + "source": [ + "### Data cleaning/Preprocessing (skipped)\n", + "Data cleaning and preprocessing is an important stage in the the data analysis pipeline. However it will be out of the scope of this notebook. For more information about data cleaning and preprocessing basics, here are some references. \n", + "* [Xarray Fundamentals](https://earth-env-data-science.github.io/lectures/xarray/xarray.html)\n", + "* [Xarray Tutorial — Pangeo Gallery documentation](http://gallery.pangeo.io/repos/pangeo-data/pangeo-tutorial-gallery/xarray.html)\n", + "* [Pythonic Data Cleaning With pandas and NumPy](https://realpython.com/python-data-cleaning-numpy-pandas/)\n", + "* [Pandas - Cleaning Data](https://www.w3schools.com/python/pandas/pandas_cleaning.asp)" + ] + }, + { + "cell_type": "markdown", + "id": "ae6e1a69", + "metadata": {}, + "source": [ + "### Plotting\n", + "Data visualization (or plotting) is another important data analysis topic and deserves its own discussion. Here in this notebook we will only demonstrate basic tool for simple data visualization tasks.\n", + "\n", + "Here are some reference you might find useful:\n", + "* xarray plotting: https://docs.xarray.dev/en/stable/user-guide/plotting.html\n", + "* Atmospheric Community Toolkit (ACT): https://arm-doe.github.io/ACT/index.html\n", + "\n", + "Note: this notebook is auto-generated using a template. It uses a general idea to select variable(s) to plot and is not customized to each indivdual notebook. Feel free to change the variables in intrrests, especially certain figure is failed to plot." + ] + }, + { + "cell_type": "markdown", + "id": "7e417fb2", + "metadata": {}, + "source": [ + "#### 1-dimenssional basic time series plot\n", + "\n", + "For the following plot we would like to find variables such that\n", + "* it has one and only one dimession\n", + "* \"time\" is its coordinate variable\n", + "* it is not a dimenssion itself,\n", + "* it is not a special variable with substrings within [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "\n", + "Please see the following example in action. (For more details about pandas filtering, please see the following references.)\n", + "* [pandas: multiple conditions while indexing data frame](https://stackoverflow.com/questions/22591174/pandas-multiple-conditions-while-indexing-data-frame-unexpected-behavior)\n", + "* [pandas.DataFrame.apply](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.apply.html)\n", + "* [How to test if a string contains one of the substrings in a list...](https://stackoverflow.com/questions/26577516/how-to-test-if-a-string-contains-one-of-the-substrings-in-a-list-in-pandas" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30edac2d", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==1, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter = df_info[(df_info.n_dim == 1) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4777a659", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_1d = df_filter.var_name.values[0]\n", + " var_1d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb5ae985", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " ds[var_1d].plot()\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "9e41066e", + "metadata": {}, + "source": [ + "#### 2-dimenssional basic plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09bd4adc", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==2, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_2 = df_info[(df_info.n_dim == 2) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2cdea1a9", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_2d = df_filter_2.var_name.values[0]\n", + " var_2d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5f48879", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " print(ds[var_2d].dims)\n", + " # ds[var_2d].plot()\n", + "\n", + " # conventionally, use \"time\" as x-axis\n", + " ds[var_2d].plot(x=\"time\")\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "7554b2a6", + "metadata": {}, + "source": [ + "#### qc-plotting (optional)\n", + "\n", + "Note: act qc plotting has more strict requirement, one of them is the associated qc variable need to have \"flag_masks\" attributes. Which is added by using ds = act.io.armfiles.read_netcdf(files_list), then ds.clean.cleanup()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71c2096f", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " ds_act = act.io.armfiles.read_netcdf(full_path)\n", + " print(type(ds_act))\n", + " ds_act.clean.cleanup()\n", + "\n", + " # or \n", + " # ds.clean.cleanup()\n", + "except Exception as e:\n", + " print(\"ERROR\", e)\n", + " ds_act = ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "844f8505", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter valid variables for ACT qc plotting\n", + "condition = (\"qc_\" + df_info.var_name).apply(lambda x: ds_act[x].attrs.get(\"flag_masks\") is not None \n", + " if x in list(ds.variables) else False)\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_3 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings))) &\n", + " condition\n", + " ]\n", + "df_filter_3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd38cc6", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "try:\n", + " qc_variable = df_filter_3.var_name.values[0]\n", + " print(qc_variable)\n", + "except Exception as e:\n", + " print(e)\n", + " \n", + "try:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + " qc_ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "d76e4d27", + "metadata": {}, + "source": [ + "#### bonus: choose variables to plot from a dropdown menu " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb733804", + "metadata": {}, + "outputs": [], + "source": [ + "# Valid variables filtering\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_4 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82d5c20d", + "metadata": {}, + "outputs": [], + "source": [ + "# example 1: using xarray plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2fc9aaa4", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# fig, ax = plt.subplots(figsize=(10, 4))\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "# fig.clear() # Remove old lines from plot and plot new one\n", + "# if len(ds[var].dims)==2:\n", + "# ds[var].plot(x=\"time\", add_colorbar=False)\n", + "# else:\n", + "# ds[var].plot()\n", + "# plt.grid()\n", + "# plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fabdd802", + "metadata": {}, + "outputs": [], + "source": [ + "# example 2: using act plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "962b4186", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "\n", + "\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "\n", + "# i_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + "# i_display.add_subplots((1,), figsize=(10, 4))\n", + "# ax = i_display.plot(var, subplot_index=(0,), set_title=f\"{var} ({ds_act[var].attrs['long_name']})\",)\n", + "\n", + "# ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "# ax.grid()\n", + "# plt.show()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/VDISQUANTS/vdisquants.c1.ipynb b/VAPs/quicklook/VDISQUANTS/vdisquants.c1.ipynb new file mode 100644 index 00000000..278606b5 --- /dev/null +++ b/VAPs/quicklook/VDISQUANTS/vdisquants.c1.ipynb @@ -0,0 +1,297 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# VDISQUANTS.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/vdisquants) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'vdisquants'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2023-12-15', 'facility': 'C1', 'site': 'ena', 'start_date': '2014-10-31'}, {'end_date': '2023-12-12', 'facility': 'M1', 'site': 'epc', 'start_date': '2023-01-15'}, {'end_date': '2019-04-29', 'facility': 'M1', 'site': 'cor', 'start_date': '2018-09-26'}, {'end_date': '2022-09-29', 'facility': 'M1', 'site': 'hou', 'start_date': '2021-08-07'}, {'end_date': '2023-12-16', 'facility': 'C1', 'site': 'sgp', 'start_date': '2016-10-01'}, {'end_date': '2023-06-20', 'facility': 'E13', 'site': 'sgp', 'start_date': '2018-06-20'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'sgp', 'C1' )\n", + "\n", + "date_start = '2023-12-14'\n", + "date_end = '2023-12-16'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['rain_rate', 'reflectivity_factor_sband20c', 'reflectivity_factor_cband20c']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'rain_rate'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/WACRARSCL/WACRARSCL_tutorial.ipynb b/VAPs/quicklook/WACRARSCL/WACRARSCL_tutorial.ipynb new file mode 100644 index 00000000..05ccaaa1 --- /dev/null +++ b/VAPs/quicklook/WACRARSCL/WACRARSCL_tutorial.ipynb @@ -0,0 +1,838 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# ARSCLWACR1KOLLIAS.C1 Notebook\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/wacrarscl) for more information about this vap." + ] + }, + { + "cell_type": "markdown", + "id": "97097763", + "metadata": {}, + "source": [ + "In this notebook, we demonstrate the workflow to explore ARM vap data (using arsclwacr1kollias as an example.) Value-added products (VAPs) are higher-order data products that have been analyzed and processed to ease scientists’ use of ARM data in atmospheric research and global climate models. \n", + "Here is the main content we will cover." + ] + }, + { + "cell_type": "markdown", + "id": "eddec40f", + "metadata": {}, + "source": [ + "# Table of Content\n", + "## Access the data\n", + "* How to retrieve the data\n", + "* Data path and file name conventions\n", + "* Load data\n", + "## Explore the data\n", + "* NetCDF Data structure\n", + "* Xarray essentials\n", + "* Xarray Variable\n", + "## Plot the data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "import random\n", + "\n", + "import glob\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "2372d5be", + "metadata": {}, + "source": [ + "## Access the data" + ] + }, + { + "cell_type": "markdown", + "id": "5b0d9684", + "metadata": {}, + "source": [ + "### How to retrieve the data\n", + "We assume the path \"/data/archive\" is available where you are running this notebook. You can use `os.path.exists(\"/data/archive\")` to verify if the path exists at your machine.\n", + "\n", + "\n", + "### Data path and file name conventions\n", + "There several common terminologies regarding ARM data, for example, data-stream-name, data-level, site, facility, instrument, etc. (For more details, please see [ARM Data File Standards Version 1.2](https://www.google.com/search?q=arm+datastream+facility+cite+definition&rlz=1C1GCEJ_enUS1029US1029&ei=hb41ZICrBPukqtsPvMODIA&ved=0ahUKEwjAgeiS0aL-AhV7kmoFHbzhAAQQ4dUDCBA&uact=5&oq=arm+datastream+facility+cite+definition&gs_lcp=Cgxnd3Mtd2l6LXNlcnAQAzIFCAAQogQyBQgAEKIEMgUIABCiBDIFCAAQogQyBQgAEKIEOgoIABBHENYEELADOgoIIRCgARDDBBAKSgQIQRgAUJ4DWIoOYO0PaAFwAXgAgAGMAYgBuQmSAQMyLjmYAQCgAQHIAQjAAQE&sclient=gws-wiz-serp).) \n", + "\n", + "For example, this notebook is called `arsclwacr1kollias.c1`, where `arsclwacr1kollias` is the \"datastream name\", and the `{process.ds_class_level}}` is the \"data level\".\n", + "\n", + "This datastream also contains site `hfe` and facility `M1`. (Note: individual datastream might have multiple site-facility pairs.)\n", + "In such a case, the data of this data-stream is stored at `/data/archive/hfe/hfearsclwacr1kolliasM1.c1`, which is in the format of `//.`. We can use the following method to assign the data-stream directory `datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )`\n", + "\n", + "The data files under datastream_dir also follows naming conventions. But once reach the datastream_dir level, the most import file naming convention to differentiate the files is \"yyyyMMdd.hhmmss\", which comes handy to filter out files based on datetime. For example, we can use `glob.glob(f'{datastream_dir}/*.200709*.*')` to filter files in 2007 September.\n", + "\n", + "(Note: refer to the https://adc.arm.gov/discovery/#/ and https://adc.arm.gov/solr8/metadata/select API to explore ARM datastream and assoicated available sites and facilities\n", + "\n", + "Please see the following examples in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d7e9eb85", + "metadata": {}, + "outputs": [], + "source": [ + "# Verify if DATA_DIR path exists\n", + "DATA_DIR = \"/data/archive\"\n", + "os.path.exists(DATA_DIR)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "586993fd", + "metadata": {}, + "outputs": [], + "source": [ + "# Speicify datastream_dir following the path conventions and check its existence\n", + "DATASTREAM_NAME = \"arsclwacr1kollias\"\n", + "DATA_LEVEL = \"c1\"\n", + "site = \"hfe\"\n", + "facility = \"M1\"\n", + "datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )\n", + "print(datastream_dir)\n", + "print(os.path.exists(datastream_dir))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0742f7c1", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: list 5 (random) files under datastream_dir\n", + "files = os.listdir(datastream_dir)\n", + "files[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39b98a36", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: get most recent file\n", + "list_of_files = glob.glob(f\"{datastream_dir}/*\") # * means all if need specific format then *.csv\n", + "latest_file = max(list_of_files, key=os.path.getctime)\n", + "latest_file" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "902d514e", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus sort datastream files based on datetime\n", + "files = os.listdir(datastream_dir)\n", + "file_sorted = files.copy()\n", + "file_sorted.sort() \n", + "print(file_sorted[:5])\n", + "\n", + "# to reverse\n", + "file_sorted_reverse = files.copy() \n", + "file_sorted_reverse.sort(reverse=True)\n", + "print(file_sorted_reverse[:5])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec5923b2", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: pattern matching\n", + "# filter the 200709** files under datastream_dir\n", + "files_filter = glob.glob(f'{datastream_dir}/*.200709*.*')\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c4a4aa18", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: use armnotebook_utils.file_filter (TODO) to filter files based on datastream info \n" + ] + }, + { + "cell_type": "markdown", + "id": "cfeb9efc", + "metadata": {}, + "source": [ + "### Load data\n", + "The arm data is stored in [NetCDF](https://en.wikipedia.org/wiki/NetCDF#:~:text=%22NetCDF%20(network%20Common%20Data%20Form,format%20for%20representing%20scientific%20data.) format. We can use xarray's `open_dataset` method to load single file, or `open_mfdataset` to open multiple files. (Note: the latter will still return a single xarray dataset object by combining multiple files.)\n", + "\n", + "Note: open_dataset keeps the file handle open and lazy loads its contents. All parameters are passed directly to open_dataset. It is a preferable method over load_dataset for memory-effiency. (more details, please see [xarray.open_dataset](https://docs.xarray.dev/en/stable/generated/xarray.open_dataset.html))\n", + "\n", + "See the following example in action" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a440a329", + "metadata": {}, + "outputs": [], + "source": [ + "# open single file\n", + "full_path = latest_file\n", + "print(full_path)\n", + "ds_single = xr.open_dataset(full_path)\n", + "ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0143a3d", + "metadata": {}, + "outputs": [], + "source": [ + "print(ds_single)\n", + "print(type(ds_single))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c0f8939", + "metadata": {}, + "outputs": [], + "source": [ + "# open multiple files\n", + "n_files = 3\n", + "full_paths = [os.path.join(datastream_dir, f_path) for f_path in file_sorted_reverse[:n_files]]\n", + "print(full_paths)\n", + "try: # Note: sometimes multiple files cannot be merged, so we used try except here.\n", + " ds_mutiple = xr.open_mfdataset(full_paths)\n", + " # ds_mutiple\n", + " print(type(ds_mutiple))\n", + " print(ds_mutiple)\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "3fa59943", + "metadata": {}, + "source": [ + "## Explore the data" + ] + }, + { + "cell_type": "markdown", + "id": "571f69b9", + "metadata": {}, + "source": [ + "### NetCDF Data structure \n", + "\n", + "\n", + "(If you are confident with NetCDF basics and xarray essentials, feel free to skip this session.)\n", + "Before we dive into data exploration, there are some eseential concpets we should be familiar with (shown below): \n", + "* Dataset\n", + "* Data array\n", + "* Variable\n", + "* Dimenssion\n", + "* Coordinate\n", + "* Data Type\n", + "* Meta Data (Attributes)\n", + "\n", + "We will not go into details about NetCDF basics and here are some references you might find helpful\n", + "* [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#)\n", + "* [Network Common Data Form (NetCDF)](https://www.unidata.ucar.edu/software/netcdf/) and [A Brief History of (netCDF) Time](https://www.unidata.ucar.edu/software/netcdf/time/recs.html)\n", + "\n", + "\n", + "\n", + "### Xarray essentials\n", + "Earlier we introduced the xarray pacakge and used `open_dataset` and `open_mfdataset` to retrieve NetCDF data file as an xarray Dataset object. Recall that you can review the data in a notebook by using `print(ds)` or `ds`. \n", + "\n", + "Using xarray to retrieve the aforementioned NetCDF basics is straightforwared. In general,\n", + "* Dataset: `ds`\n", + "* Data array: `ds.variables`\n", + "* Variable: `ds.ds.variables`\n", + "* Dimenssion: `ds.dims`\n", + "* Coordinate: `ds.coords`\n", + "* Data Type: `type`\n", + "* Meta Data (Attributes)\n", + "\n", + "Also, here are some references if you are new to xarray\n", + "* [Xarray in 45 minutes](https://tutorial.xarray.dev/overview/xarray-in-45-min.html)\n", + "* [Handling NetCDF Files using XArray for Absolute Beginners](https://towardsdatascience.com/handling-netcdf-files-using-xarray-for-absolute-beginners-111a8ab4463f)\n", + "\n", + "Try the following commands in action\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b3eac323", + "metadata": {}, + "outputs": [], + "source": [ + "ds = ds_single" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77ecf85d", + "metadata": {}, + "outputs": [], + "source": [ + "# Dataset \n", + "ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25e7de09", + "metadata": {}, + "outputs": [], + "source": [ + "# Data array, Variable\n", + "# Note: the info can be overwhelming. Do not attempt to grasp everything at the first glance.\n", + "ds.variables" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c41b67e", + "metadata": {}, + "outputs": [], + "source": [ + "# dimenssions\n", + "ds.dims" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "156f1dfc", + "metadata": {}, + "outputs": [], + "source": [ + "# coordinates\n", + "ds.coords" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "277d6064", + "metadata": {}, + "outputs": [], + "source": [ + "# Meta Data (Attributes)\n", + "ds.attrs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d334681f", + "metadata": {}, + "outputs": [], + "source": [ + "# type\n", + "print(type(ds))\n", + "print(type(ds.variables))\n", + "print(type(ds.dims))\n", + "print(type(ds.coords))\n", + "print(type(ds.attrs))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "643399d6", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: retrieve variable names only\n", + "list(ds.variables)" + ] + }, + { + "cell_type": "markdown", + "id": "1cf36878", + "metadata": {}, + "source": [ + "#### Discussion: variable vs. coordinates vs. dimenssions. \n", + "From [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#):\n", + "\n", + "* Dimession: A dimension may be used to represent a real physical dimension, for example, time, latitude, longitude, or height. A dimension might also be used to index other quantities, for example station or model-run-number.\n", + "A netCDF dimension has both a name and a length. A dimension length is an arbitrary positive integer, except that one dimension in a netCDF dataset can have the length UNLIMITED.\n", + "\n", + "* Variables: Variables are used to store the bulk of the data in a netCDF dataset. A variable represents an array of values of the same type. A scalar value is treated as a 0-dimensional array. A variable has a name, a data type, and a shape described by its list of dimensions specified when the variable is created. A variable may also have associated attributes, which may be added, deleted or changed after the variable is created.\n", + "\n", + "* Coordinate (Variables): It is legal for a variable to have the same name as a dimension. Such variables have no special meaning to the netCDF library. However there is a convention that such variables should be treated in a special way by software using this library.\n", + "A variable with the same name as a dimension is called a coordinate variable. It typically defines a physical coordinate corresponding to that dimension.\n", + "\n", + "\n", + "Tips: By definition, coordinates and dimenssions are also variable. For example ds.time is a coordinate but it is also a special variable. But in practice, when we talk about variable, it implies regular/non-coordinate variable. We will use this convention for the remaining notebook. " + ] + }, + { + "cell_type": "markdown", + "id": "d47120f9", + "metadata": {}, + "source": [ + "### Xarray Variable\n", + "\n", + "\n", + "We can use `ds.variables` to access varialbes of a dataset. But many times viewing print-out the whole datasets can be overwhemling. Instead, we would work on the individual variable (also called Xarray data array.) using the `ds[var_name]` syntax, where `var_name` is the name of the variable. See the following examples in action.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5a57e136", + "metadata": {}, + "outputs": [], + "source": [ + "print(type(ds[\"time\"]))\n", + "ds[\"time\"]" + ] + }, + { + "cell_type": "markdown", + "id": "51ec643e", + "metadata": {}, + "source": [ + "#### Variable properties\n", + "For individual varible, the following properties are mostly used with the assocaited xarray method to retrieve them. (assuming var = ds[var_name])\n", + "* name: `var.name`\n", + "* data content: `var.data`\n", + "* attributes: `var.attrs`\n", + "* dimenstions: `var.dims`\n", + "* data type: `var.data.dtype`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a632a525", + "metadata": {}, + "outputs": [], + "source": [ + "var_name = \"time\"\n", + "var = ds[var_name]\n", + "\n", + "print(\"var.name: \\n\", var.name, \"\\n\")\n", + "print(\"var.data: \\n\", var.data, \"\\n\")\n", + "print(\"var.attrs: \\n\", var.attrs, \"\\n\")\n", + "print(\"var.dims: \\n\", var.dims, \"\\n\")\n", + "print(\"var.data.dtype: \\n\", var.data.dtype, \"\\n\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b256b07f", + "metadata": {}, + "outputs": [], + "source": [ + "# bonus: indivdual variable info table (collect and display variable info as a tabular format)\n", + "df_info = pd.DataFrame()\n", + "df_info[\"var_name\"] = list(ds.variables)\n", + "df_info[\"dims\"] = df_info.var_name.apply(lambda x: ds[x].dims)\n", + "df_info[\"is_dim\"] = df_info.var_name.apply(lambda x: x in ds.dims)\n", + "df_info[\"n_dim\"] = df_info.var_name.apply(lambda x: len(ds[x].dims))\n", + "df_info[\"attrs\"] = df_info.var_name.apply(lambda x: ds[x].attrs)\n", + "df_info[\"dtype\"] = df_info.var_name.apply(lambda x: ds[x].data.dtype)\n", + "df_info" + ] + }, + { + "cell_type": "markdown", + "id": "e6a36b96", + "metadata": {}, + "source": [ + "### Data cleaning/Preprocessing (skipped)\n", + "Data cleaning and preprocessing is an important stage in the the data analysis pipeline. However it will be out of the scope of this notebook. For more information about data cleaning and preprocessing basics, here are some references. \n", + "* [Xarray Fundamentals](https://earth-env-data-science.github.io/lectures/xarray/xarray.html)\n", + "* [Xarray Tutorial — Pangeo Gallery documentation](http://gallery.pangeo.io/repos/pangeo-data/pangeo-tutorial-gallery/xarray.html)\n", + "* [Pythonic Data Cleaning With pandas and NumPy](https://realpython.com/python-data-cleaning-numpy-pandas/)\n", + "* [Pandas - Cleaning Data](https://www.w3schools.com/python/pandas/pandas_cleaning.asp)" + ] + }, + { + "cell_type": "markdown", + "id": "ae6e1a69", + "metadata": {}, + "source": [ + "### Plotting\n", + "Data visualization (or plotting) is another important data analysis topic and deserves its own discussion. Here in this notebook we will only demonstrate basic tool for simple data visualization tasks.\n", + "\n", + "Here are some reference you might find useful:\n", + "* xarray plotting: https://docs.xarray.dev/en/stable/user-guide/plotting.html\n", + "* Atmospheric Community Toolkit (ACT): https://arm-doe.github.io/ACT/index.html\n", + "\n", + "Note: this notebook is auto-generated using a template. It uses a general idea to select variable(s) to plot and is not customized to each indivdual notebook. Feel free to change the variables in intrrests, especially certain figure is failed to plot." + ] + }, + { + "cell_type": "markdown", + "id": "7e417fb2", + "metadata": {}, + "source": [ + "#### 1-dimenssional basic time series plot\n", + "\n", + "For the following plot we would like to find variables such that\n", + "* it has one and only one dimession\n", + "* \"time\" is its coordinate variable\n", + "* it is not a dimenssion itself,\n", + "* it is not a special variable with substrings within [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "\n", + "Please see the following example in action. (For more details about pandas filtering, please see the following references.)\n", + "* [pandas: multiple conditions while indexing data frame](https://stackoverflow.com/questions/22591174/pandas-multiple-conditions-while-indexing-data-frame-unexpected-behavior)\n", + "* [pandas.DataFrame.apply](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.apply.html)\n", + "* [How to test if a string contains one of the substrings in a list...](https://stackoverflow.com/questions/26577516/how-to-test-if-a-string-contains-one-of-the-substrings-in-a-list-in-pandas" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30edac2d", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==1, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter = df_info[(df_info.n_dim == 1) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4777a659", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_1d = df_filter.var_name.values[0]\n", + " var_1d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb5ae985", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " ds[var_1d].plot()\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "9e41066e", + "metadata": {}, + "source": [ + "#### 2-dimenssional basic plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09bd4adc", + "metadata": {}, + "outputs": [], + "source": [ + "# (pandas query) filter n_dim==2, non-dimenssional, and not contains the following substrings\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_2 = df_info[(df_info.n_dim == 2) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (df_info.is_dim==False) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2cdea1a9", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " var_2d = df_filter_2.var_name.values[0]\n", + " var_2d\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5f48879", + "metadata": {}, + "outputs": [], + "source": [ + "# Note: if failed, change to another variable to plot.\n", + "try:\n", + " print(ds[var_2d].dims)\n", + " # ds[var_2d].plot()\n", + "\n", + " # conventionally, use \"time\" as x-axis\n", + " ds[var_2d].plot(x=\"time\")\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e) " + ] + }, + { + "cell_type": "markdown", + "id": "7554b2a6", + "metadata": {}, + "source": [ + "#### qc-plotting (optional)\n", + "\n", + "Note: act qc plotting has more strict requirement, one of them is the associated qc variable need to have \"flag_masks\" attributes. Which is added by using ds = act.io.armfiles.read_netcdf(files_list), then ds.clean.cleanup()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71c2096f", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " ds_act = act.io.armfiles.read_netcdf(full_path)\n", + " print(type(ds_act))\n", + " ds_act.clean.cleanup()\n", + "\n", + " # or \n", + " # ds.clean.cleanup()\n", + "except Exception as e:\n", + " print(\"ERROR\", e)\n", + " ds_act = ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "844f8505", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter valid variables for ACT qc plotting\n", + "condition = (\"qc_\" + df_info.var_name).apply(lambda x: ds_act[x].attrs.get(\"flag_masks\") is not None \n", + " if x in list(ds.variables) else False)\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_3 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings))) &\n", + " condition\n", + " ]\n", + "df_filter_3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecd38cc6", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "try:\n", + " qc_variable = df_filter_3.var_name.values[0]\n", + " print(qc_variable)\n", + "except Exception as e:\n", + " print(e)\n", + " \n", + "try:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + " qc_ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "\n", + " plt.show()\n", + "except Exception as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "id": "d76e4d27", + "metadata": {}, + "source": [ + "#### bonus: choose variables to plot from a dropdown menu " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb733804", + "metadata": {}, + "outputs": [], + "source": [ + "# Valid variables filtering\n", + "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", + "df_filter_4 = df_info[(df_info.is_dim==False) &\n", + " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", + " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", + " ]\n", + "df_filter_4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82d5c20d", + "metadata": {}, + "outputs": [], + "source": [ + "# example 1: using xarray plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2fc9aaa4", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# fig, ax = plt.subplots(figsize=(10, 4))\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "# fig.clear() # Remove old lines from plot and plot new one\n", + "# if len(ds[var].dims)==2:\n", + "# ds[var].plot(x=\"time\", add_colorbar=False)\n", + "# else:\n", + "# ds[var].plot()\n", + "# plt.grid()\n", + "# plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fabdd802", + "metadata": {}, + "outputs": [], + "source": [ + "# example 2: using act plot\n", + "\n", + "# Uncomment the following cell to try the interactive plot (ctrl + /)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "962b4186", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib widget\n", + "# plt.clf()\n", + "\n", + "# available_variables = df_filter_4.var_name.values\n", + "\n", + "\n", + "# @widgets.interact(var=available_variables)\n", + "# def update(var = available_variables[0]):\n", + "\n", + "# i_display = act.plotting.TimeSeriesDisplay(ds_act)\n", + "# i_display.add_subplots((1,), figsize=(10, 4))\n", + "# ax = i_display.plot(var, subplot_index=(0,), set_title=f\"{var} ({ds_act[var].attrs['long_name']})\",)\n", + "\n", + "# ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", + "# ax.grid()\n", + "# plt.show()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/WACRARSCL/arsclwacr1kollias.c1.ipynb b/VAPs/quicklook/WACRARSCL/arsclwacr1kollias.c1.ipynb new file mode 100644 index 00000000..b2a8d160 --- /dev/null +++ b/VAPs/quicklook/WACRARSCL/arsclwacr1kollias.c1.ipynb @@ -0,0 +1,339 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# ARSCLWACR1KOLLIAS.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/wacrarscl) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'arsclwacr1kollias'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2008-12-15', 'facility': 'M1', 'site': 'hfe', 'start_date': '2008-10-21'}, {'end_date': '2008-01-01', 'facility': 'M1', 'site': 'fkb', 'start_date': '2007-03-29'}, {'end_date': '2018-03-24', 'facility': 'M1', 'site': 'mar', 'start_date': '2017-10-29'}, {'end_date': '2010-12-31', 'facility': 'M1', 'site': 'grw', 'start_date': '2009-06-06'}, {'end_date': '2006-12-29', 'facility': 'M1', 'site': 'nim', 'start_date': '2006-03-16'}, {'end_date': '2013-06-14', 'facility': 'M1', 'site': 'pvc', 'start_date': '2012-10-12'}, {'end_date': '2014-09-13', 'facility': 'M1', 'site': 'tmp', 'start_date': '2014-02-01'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'hfe', 'M1' )\n", + "\n", + "date_start = '2008-12-13'\n", + "date_end = '2008-12-15'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['reflectivity', 'reflectivity_best_estimate', 'mean_doppler_velocity']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'radar_first_top'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'reflectivity'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/quicklook/WACRARSCL/arsclwacrbnd1kollias.c1.ipynb b/VAPs/quicklook/WACRARSCL/arsclwacrbnd1kollias.c1.ipynb new file mode 100644 index 00000000..fde3c46a --- /dev/null +++ b/VAPs/quicklook/WACRARSCL/arsclwacrbnd1kollias.c1.ipynb @@ -0,0 +1,339 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "70840257-70e4-45e2-b491-14bff5a257a3", + "metadata": {}, + "source": [ + "# ARSCLWACRBND1KOLLIAS.C1 Plots\n", + "\n", + "[Click here](https://www.arm.gov/capabilities/vaps/wacrarscl) for more information about this vap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "460fd89f-e034-452c-b837-f65c5958264f", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import ipywidgets as widgets\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import ipywidgets as widgets\n", + "import numpy as np\n", + "import pandas as pd\n", + "import os\n", + "from datetime import datetime\n", + "\n", + "import act\n", + "import xarray as xr\n", + "\n", + "# Data archive directory\n", + "DATA_DIR = r'/data/archive/'\n", + "\n", + "# Datastream info\n", + "DATASTREAM_NAME = 'arsclwacrbnd1kollias'\n", + "DATA_LEVEL = 'c1'\n", + "LOCATIONS = [{'end_date': '2008-01-02', 'facility': 'M1', 'site': 'fkb', 'start_date': '2007-03-29'}, {'end_date': '2018-03-24', 'facility': 'M1', 'site': 'mar', 'start_date': '2017-10-29'}, {'end_date': '2008-12-15', 'facility': 'M1', 'site': 'hfe', 'start_date': '2008-10-14'}, {'end_date': '2010-12-31', 'facility': 'M1', 'site': 'grw', 'start_date': '2009-06-06'}, {'end_date': '2006-12-29', 'facility': 'M1', 'site': 'nim', 'start_date': '2006-03-16'}, {'end_date': '2013-06-14', 'facility': 'M1', 'site': 'pvc', 'start_date': '2012-10-12'}, {'end_date': '2014-09-13', 'facility': 'M1', 'site': 'tmp', 'start_date': '2014-02-01'}]" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "9faaf875", + "metadata": {}, + "source": [ + "## Define site, facility, and date range" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac6764f5", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"The following locations and date ranges are available for this VAP:\")\n", + "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8d132223", + "metadata": {}, + "source": [ + "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e563983a", + "metadata": {}, + "outputs": [], + "source": [ + "site_facility = ( 'fkb', 'M1' )\n", + "\n", + "date_start = '2007-12-31'\n", + "date_end = '2008-01-02'" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", + "metadata": {}, + "source": [ + "## Load data files\n", + "Load data files from /data/archive/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", + "metadata": {}, + "outputs": [], + "source": [ + "# Compile list of files\n", + "site, facility = site_facility\n", + "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", + "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", + "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", + "dir_path\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6be8f3dc", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import date, timedelta\n", + "import pandas as pd\n", + "\n", + "def get_ARM_formated_dates(start_date, end_date):\n", + " \"\"\"\n", + " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", + " EXAMPLE:\n", + " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", + " >> [\"20180219\", \"20180220\", \"20180221\"] \n", + " \"\"\"\n", + " \n", + " _start_date = pd.to_datetime(start_date)\n", + " _end_date = pd.to_datetime(end_date)\n", + " \n", + " delta = _end_date - _start_date # returns timedelta \n", + " dates = []\n", + "\n", + " for i in range(delta.days + 1):\n", + " day = _start_date + timedelta(days=i)\n", + " day_formated = day.strftime(format=\"%Y%m%d\")\n", + " dates.append(day_formated)\n", + " return dates\n", + "\n", + "\n", + "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51feea2e", + "metadata": {}, + "outputs": [], + "source": [ + "# Filter a list of files based on date pattern\n", + "import glob\n", + "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", + "files_filter = []\n", + "for date in dates:\n", + " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", + " files_filter\n", + "files_filter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0e5d0d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Load files as a single dataset\n", + "files_list = files_filter \n", + "ds = act.io.armfiles.read_netcdf(files_list)\n", + "ds.clean.cleanup()\n", + "print(f'{len(files_list)} files loaded')\n", + "ds\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", + "metadata": {}, + "source": [ + "## Plot time series data\n", + "#### Define the list of variables to be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d09b789e-84f1-4605-846b-a72c110c8048", + "metadata": {}, + "outputs": [], + "source": [ + "variables_to_plot = ['cloud_base_best_estimate', 'cloud_layer_base_height', 'cloud_layer_top_height']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", + "metadata": {}, + "outputs": [], + "source": [ + "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", + "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", + "\n", + "for i,v in enumerate(variables_to_plot):\n", + " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", + " ts_ax.grid()\n", + "\n", + "plt.show()\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "194399aa-1907-452b-8ba9-bc31d7f60291", + "metadata": {}, + "source": [ + "## Quality check plots\n", + "#### Define variable for QC plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", + "metadata": {}, + "outputs": [], + "source": [ + "qc_variable = 'radar_first_top'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", + "metadata": {}, + "outputs": [], + "source": [ + "# QC Plot\n", + "if ('qc_'+qc_variable) in ds.variables:\n", + "\n", + " # Plot\n", + " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", + " qc_display.add_subplots((2,), figsize = (9.5,10))\n", + " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", + " qc_ax.grid()\n", + " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", + "\n", + " plt.show()\n", + "else:\n", + " print(f'QC not available for the selected field: {qc_variable}')\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", + "metadata": {}, + "source": [ + "## Field selection dropdown menu\n", + "Select variable to be plotted from a dropdown menu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "\n", + "# populate dropdown menu with available variables \n", + "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", + "d_variable = 'cloud_base_best_estimate'\n", + "dropdown = widgets.Dropdown(\n", + " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", + " value= d_variable,\n", + " description='Field:',\n", + " disabled=False,\n", + ")\n", + "dropdown.layout.margin = '0px 30% 0px 20%'\n", + "dropdown.layout.width = '50%'\n", + "\n", + "# set up display\n", + "i_display = act.plotting.TimeSeriesDisplay(ds)\n", + "i_display.add_subplots((1,), figsize = (9.5,5))\n", + "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", + "i_ax.grid()\n", + "i_fig = i_display.fig\n", + "\n", + "# update plot callback function\n", + "def update_plot(change):\n", + " i_ax.cla()\n", + " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", + " i_ax_new.grid()\n", + " i_fig.canvas.draw()\n", + " i_fig.canvas.flush_events()\n", + "\n", + "dropdown.observe(update_plot, names='value')\n", + "\n", + "widgets.AppLayout(\n", + " header=dropdown,\n", + " center=i_fig.canvas,\n", + " pane_heights=[1, 6,1]\n", + ")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/VAPs/vap_notebook_list.md b/VAPs/vap_notebook_list.md new file mode 100644 index 00000000..a94c2ea7 --- /dev/null +++ b/VAPs/vap_notebook_list.md @@ -0,0 +1,1010 @@ +# ARM VAPs Jupyter Notebooks +List of available ARM VAPs Notebooks. + +You must have access to the [ARM JupyterHub Server](https://jupyterhub.arm.gov/) to open these notebooks. + + + +
+ 2DS-AIR + +* [2DS-AIR tutorial](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/2DS-AIR/2DS-AIR_tutorial.ipynb&branch=main) + +* [aaf2dsh.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/2DS-AIR/aaf2dsh.c1.ipynb&branch=main) + +* [aaf2dsv.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/2DS-AIR/aaf2dsv.c1.ipynb&branch=main) + + +
+ + +
+ ACSMCDCE + +* [ACSMCDCE tutorial](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/ACSMCDCE/ACSMCDCE_tutorial.ipynb&branch=main) + +* [acsmcdce.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/ACSMCDCE/acsmcdce.c1.ipynb&branch=main) + +* [acsmcdce.c2](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/ACSMCDCE/acsmcdce.c2.ipynb&branch=main) + +* [acsmtofcdce.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/ACSMCDCE/acsmtofcdce.c1.ipynb&branch=main) + + +
+ + +
+ AERINF + +* [AERINF tutorial](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/AERINF/AERINF_tutorial.ipynb&branch=main) + +* [aerich1nf1turn.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/AERINF/aerich1nf1turn.c1.ipynb&branch=main) + +* [aerich2nf1turn.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/AERINF/aerich2nf1turn.c1.ipynb&branch=main) + + +
+ + +
+ AERIOE + +* [AERIOE tutorial](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/AERIOE/AERIOE_tutorial.ipynb&branch=main) + +* [aerioe1turn.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/AERIOE/aerioe1turn.c1.ipynb&branch=main) + + +
+ + +
+ AERIPROF + +* [AERIPROF tutorial](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/AERIPROF/AERIPROF_tutorial.ipynb&branch=main) + +* [aeri01prof3feltz.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/AERIPROF/aeri01prof3feltz.c1.ipynb&branch=main) + +* [aeriprof3feltz.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/AERIPROF/aeriprof3feltz.c1.ipynb&branch=main) + +* [qmeaeriprof.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/AERIPROF/qmeaeriprof.c1.ipynb&branch=main) + + +
+ + +
+ AEROSOLBE + +* [AEROSOLBE tutorial](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/AEROSOLBE/AEROSOLBE_tutorial.ipynb&branch=main) + +* [aerosolbe1turn.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/AEROSOLBE/aerosolbe1turn.c1.ipynb&branch=main) + + +
+ + +
+ AIP + +* [AIP tutorial](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/AIP/AIP_tutorial.ipynb&branch=main) + +* [aip1ogren.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/AIP/aip1ogren.c1.ipynb&branch=main) + +* [aipavg1ogren.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/AIP/aipavg1ogren.c1.ipynb&branch=main) + +* [aipfitrh1ogren.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/AIP/aipfitrh1ogren.c1.ipynb&branch=main) + + +
+ + +
+ AOD + +* [AOD tutorial](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/AOD/AOD_tutorial.ipynb&branch=main) + +* [sasheniraod.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/AOD/sasheniraod.c1.ipynb&branch=main) + +* [sashevisaod.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/AOD/sashevisaod.c1.ipynb&branch=main) + + +
+ + +
+ AOD-MFRSR + +* [AOD-MFRSR tutorial](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/AOD-MFRSR/AOD-MFRSR_tutorial.ipynb&branch=main) + +* [mfrsr7nchaod1mich.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/AOD-MFRSR/mfrsr7nchaod1mich.c1.ipynb&branch=main) + +* [mfrsr7nchcal.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/AOD-MFRSR/mfrsr7nchcal.c1.ipynb&branch=main) + +* [mfrsraod1mich.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/AOD-MFRSR/mfrsraod1mich.c1.ipynb&branch=main) + +* [mfrsrcal.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/AOD-MFRSR/mfrsrcal.c1.ipynb&branch=main) + + +
+ + +
+ AOD-NIMFR + +* [AOD-NIMFR tutorial](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/AOD-NIMFR/AOD-NIMFR_tutorial.ipynb&branch=main) + +* [nimfraod1mich.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/AOD-NIMFR/nimfraod1mich.c1.ipynb&branch=main) + + +
+ + +
+ AOP + +* [AOP tutorial](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/AOP/AOP_tutorial.ipynb&branch=main) + +* [aopclap1flynn1m.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/AOP/aopclap1flynn1m.c1.ipynb&branch=main) + +* [aoppsap1flynn1h.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/AOP/aoppsap1flynn1h.c1.ipynb&branch=main) + +* [aoppsap1flynn1m.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/AOP/aoppsap1flynn1m.c1.ipynb&branch=main) + + +
+ + +
+ AOSCCNAVG + +* [AOSCCNAVG tutorial](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/AOSCCNAVG/AOSCCNAVG_tutorial.ipynb&branch=main) + +* [aosccnavg.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/AOSCCNAVG/aosccnavg.c1.ipynb&branch=main) + +* [aosccnavg.c2](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/AOSCCNAVG/aosccnavg.c2.ipynb&branch=main) + + +
+ + +
+ AOSSP2BC + +* [AOSSP2BC tutorial](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/AOSSP2BC/AOSSP2BC_tutorial.ipynb&branch=main) + +* [aossp2rbc1m.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/AOSSP2BC/aossp2rbc1m.c1.ipynb&branch=main) + + +
+ + +
+ ARMBE + +* [ARMBE tutorial](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/ARMBE/ARMBE_tutorial.ipynb&branch=main) + +* [armbeatm.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/ARMBE/armbeatm.c1.ipynb&branch=main) + +* [armbecldrad.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/ARMBE/armbecldrad.c1.ipynb&branch=main) + + +
+ + +
+ ARSCL + +* [ARSCL tutorial](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/ARSCL/ARSCL_tutorial.ipynb&branch=main) + +* [arscl1cloth.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/ARSCL/arscl1cloth.c1.ipynb&branch=main) + +* [arsclbnd1cloth.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/ARSCL/arsclbnd1cloth.c1.ipynb&branch=main) + + +
+ + +
+ ASDBE-AIR + +* [ASDBE-AIR tutorial](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/ASDBE-AIR/ASDBE-AIR_tutorial.ipynb&branch=main) + +* [aafmergedaerosolsd.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/ASDBE-AIR/aafmergedaerosolsd.c1.ipynb&branch=main) + + +
+ + +
+ BAEBBR + +* [BAEBBR tutorial](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/BAEBBR/BAEBBR_tutorial.ipynb&branch=main) + +* [30baebbr.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/BAEBBR/30baebbr.c1.ipynb&branch=main) + + +
+ + +
+ BBHRP + +* [BBHRP tutorial](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/BBHRP/BBHRP_tutorial.ipynb&branch=main) + +* [1bbhrpripbe1mcfarlane.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/BBHRP/1bbhrpripbe1mcfarlane.c1.ipynb&branch=main) + +* [30bbhrpripbe1mcfarlane.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/BBHRP/30bbhrpripbe1mcfarlane.c1.ipynb&branch=main) + +* [bbhrpavg1mlawer.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/BBHRP/bbhrpavg1mlawer.c1.ipynb&branch=main) + + +
+ + +
+ BEFLUX + +* [BEFLUX tutorial](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/BEFLUX/BEFLUX_tutorial.ipynb&branch=main) + +* [beflux1long.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/BEFLUX/beflux1long.c1.ipynb&branch=main) + +* [qcflux1long.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/BEFLUX/qcflux1long.c1.ipynb&branch=main) + + +
+ + +
+ CCNKAPPA + +* [CCNKAPPA tutorial](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/CCNKAPPA/CCNKAPPA_tutorial.ipynb&branch=main) + +* [aosccnsmpskappa.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/CCNKAPPA/aosccnsmpskappa.c1.ipynb&branch=main) + + +
+ + +
+ CCNPROF + +* [CCNPROF tutorial](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/CCNPROF/CCNPROF_tutorial.ipynb&branch=main) + +* [rlccnprof1ghan.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/CCNPROF/rlccnprof1ghan.c1.ipynb&branch=main) + + +
+ + +
+ CLAP + +* [CLAP tutorial](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/CLAP/CLAP_tutorial.ipynb&branch=main) + +* [aosclap3w.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/CLAP/aosclap3w.c1.ipynb&branch=main) + + +
+ + +
+ CLDTYPE + +* [CLDTYPE tutorial](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/CLDTYPE/CLDTYPE_tutorial.ipynb&branch=main) + +* [cldtype.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/CLDTYPE/cldtype.c1.ipynb&branch=main) + + +
+ + +
+ CMAC2 + +* [CMAC2 tutorial](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/CMAC2/CMAC2_tutorial.ipynb&branch=main) + +* [cmac2.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/CMAC2/cmac2.c1.ipynb&branch=main) + + +
+ + +
+ CO-AIR + +* [CO-AIR tutorial](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/CO-AIR/CO-AIR_tutorial.ipynb&branch=main) + +* [aafco.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/CO-AIR/aafco.c1.ipynb&branch=main) + + +
+ + +
+ COGS + +* [COGS tutorial](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/COGS/COGS_tutorial.ipynb&branch=main) + +* [cogs.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/COGS/cogs.c1.ipynb&branch=main) + + +
+ + +
+ DIFFCOR + +* [DIFFCOR tutorial](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/DIFFCOR/DIFFCOR_tutorial.ipynb&branch=main) + +* [brs1dutt.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/DIFFCOR/brs1dutt.c1.ipynb&branch=main) + +* [siros1dutt.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/DIFFCOR/siros1dutt.c1.ipynb&branch=main) + +* [sirs1dutt.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/DIFFCOR/sirs1dutt.c1.ipynb&branch=main) + + +
+ + +
+ DLPROF-WIND + +* [DLPROF-WIND tutorial](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/DLPROF-WIND/DLPROF-WIND_tutorial.ipynb&branch=main) + +* [dlprofwind4news.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/DLPROF-WIND/dlprofwind4news.c1.ipynb&branch=main) + + +
+ + +
+ DLPROF-WSTATS + +* [DLPROF-WSTATS tutorial](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/DLPROF-WSTATS/DLPROF-WSTATS_tutorial.ipynb&branch=main) + +* [dlprofwstats4news.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/DLPROF-WSTATS/dlprofwstats4news.c1.ipynb&branch=main) + + +
+ + +
+ FCDP-AIR + +* [FCDP-AIR tutorial](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/FCDP-AIR/FCDP-AIR_tutorial.ipynb&branch=main) + +* [aaffcdp.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/FCDP-AIR/aaffcdp.c1.ipynb&branch=main) + + +
+ + +
+ GVR + +* [GVR tutorial](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/GVR/GVR_tutorial.ipynb&branch=main) + +* [gvr.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/GVR/gvr.c1.ipynb&branch=main) + + +
+ + +
+ HVPS-AIR + +* [HVPS-AIR tutorial](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/HVPS-AIR/HVPS-AIR_tutorial.ipynb&branch=main) + +* [aafhvps.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/HVPS-AIR/aafhvps.c1.ipynb&branch=main) + + +
+ + +
+ INLETCVI-AIR + +* [INLETCVI-AIR tutorial](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/INLETCVI-AIR/INLETCVI-AIR_tutorial.ipynb&branch=main) + +* [aafinletcvi.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/INLETCVI-AIR/aafinletcvi.c1.ipynb&branch=main) + + +
+ + +
+ INTERPSONDE + +* [INTERPSONDE tutorial](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/INTERPSONDE/INTERPSONDE_tutorial.ipynb&branch=main) + +* [interpolatedsonde.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/INTERPSONDE/interpolatedsonde.c1.ipynb&branch=main) + + +
+ + +
+ KAZRARSCL + +* [KAZRARSCL tutorial](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/KAZRARSCL/KAZRARSCL_tutorial.ipynb&branch=main) + +* [arsclkazr1kollias.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/KAZRARSCL/arsclkazr1kollias.c1.ipynb&branch=main) + +* [arsclkazrbnd1kollias.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/KAZRARSCL/arsclkazrbnd1kollias.c1.ipynb&branch=main) + + +
+ + +
+ KAZRARSCLCLOUDSAT + +* [KAZRARSCLCLOUDSAT tutorial](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/KAZRARSCLCLOUDSAT/KAZRARSCLCLOUDSAT_tutorial.ipynb&branch=main) + +* [arsclkazrcloudsat.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/KAZRARSCLCLOUDSAT/arsclkazrcloudsat.c1.ipynb&branch=main) + + +
+ + +
+ KAZRCFRCOR + +* [KAZRCFRCOR tutorial](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/KAZRCFRCOR/KAZRCFRCOR_tutorial.ipynb&branch=main) + +* [kazrcfrcorge.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/KAZRCFRCOR/kazrcfrcorge.c1.ipynb&branch=main) + +* [kazrcfrcormd.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/KAZRCFRCOR/kazrcfrcormd.c1.ipynb&branch=main) + + +
+ + +
+ KAZRCOR + +* [KAZRCOR tutorial](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/KAZRCOR/KAZRCOR_tutorial.ipynb&branch=main) + +* [kazrcorge.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/KAZRCOR/kazrcorge.c1.ipynb&branch=main) + +* [kazrcorhi.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/KAZRCOR/kazrcorhi.c1.ipynb&branch=main) + +* [kazrcormd.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/KAZRCOR/kazrcormd.c1.ipynb&branch=main) + + +
+ + +
+ LCLHEIGHT + +* [LCLHEIGHT tutorial](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/LCLHEIGHT/LCLHEIGHT_tutorial.ipynb&branch=main) + +* [lcl.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/LCLHEIGHT/lcl.c1.ipynb&branch=main) + + +
+ + +
+ LDQUANTS + +* [LDQUANTS tutorial](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/LDQUANTS/LDQUANTS_tutorial.ipynb&branch=main) + +* [ldquants.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/LDQUANTS/ldquants.c1.ipynb&branch=main) + + +
+ + +
+ LSSONDE + +* [LSSONDE tutorial](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/LSSONDE/LSSONDE_tutorial.ipynb&branch=main) + +* [lssonde.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/LSSONDE/lssonde.c1.ipynb&branch=main) + + +
+ + +
+ MASCPARTICLES + +* [MASCPARTICLES tutorial](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/MASCPARTICLES/MASCPARTICLES_tutorial.ipynb&branch=main) + +* [mascparticles.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/MASCPARTICLES/mascparticles.c1.ipynb&branch=main) + +* [mascparticlesavg.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/MASCPARTICLES/mascparticlesavg.c1.ipynb&branch=main) + + +
+ + +
+ MERGED-COMMON + +* [MERGED-COMMON tutorial](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/MERGED-COMMON/MERGED-COMMON_tutorial.ipynb&branch=main) + +* [aafmergedcldsd.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/MERGED-COMMON/aafmergedcldsd.c1.ipynb&branch=main) + + +
+ + +
+ MERGEDSMPSAPS + +* [MERGEDSMPSAPS tutorial](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/MERGEDSMPSAPS/MERGEDSMPSAPS_tutorial.ipynb&branch=main) + +* [mergedsmpsaps.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/MERGEDSMPSAPS/mergedsmpsaps.c1.ipynb&branch=main) + + +
+ + +
+ MERGESONDE + +* [MERGESONDE tutorial](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/MERGESONDE/MERGESONDE_tutorial.ipynb&branch=main) + +* [mergesonde1mace.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/MERGESONDE/mergesonde1mace.c1.ipynb&branch=main) + +* [mergesonde2mace.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/MERGESONDE/mergesonde2mace.c1.ipynb&branch=main) + + +
+ + +
+ MFRSRCLDOD + +* [MFRSRCLDOD tutorial](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/MFRSRCLDOD/MFRSRCLDOD_tutorial.ipynb&branch=main) + +* [mfrsrcldod1min.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/MFRSRCLDOD/mfrsrcldod1min.c1.ipynb&branch=main) + + +
+ + +
+ MICROBASE + +* [MICROBASE tutorial](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/MICROBASE/MICROBASE_tutorial.ipynb&branch=main) + +* [microbasepi.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/MICROBASE/microbasepi.c1.ipynb&branch=main) + +* [microbasepi2.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/MICROBASE/microbasepi2.c1.ipynb&branch=main) + +* [microbasepiavg.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/MICROBASE/microbasepiavg.c1.ipynb&branch=main) + + +
+ + +
+ MPLAVG + +* [MPLAVG tutorial](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/MPLAVG/MPLAVG_tutorial.ipynb&branch=main) + +* [mplpolavg.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/MPLAVG/mplpolavg.c1.ipynb&branch=main) + + +
+ + +
+ MPLCMASK + +* [MPLCMASK tutorial](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/MPLCMASK/MPLCMASK_tutorial.ipynb&branch=main) + +* [30smplcmask1zwang.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/MPLCMASK/30smplcmask1zwang.c1.ipynb&branch=main) + + +
+ + +
+ MPLCMASKML + +* [MPLCMASKML tutorial](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/MPLCMASKML/MPLCMASKML_tutorial.ipynb&branch=main) + +* [mplcmaskml.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/MPLCMASKML/mplcmaskml.c1.ipynb&branch=main) + + +
+ + +
+ MPLNOR + +* [MPLNOR tutorial](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/MPLNOR/MPLNOR_tutorial.ipynb&branch=main) + +* [mplnor1camp.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/MPLNOR/mplnor1camp.c1.ipynb&branch=main) + + +
+ + +
+ MWRRET + +* [MWRRET tutorial](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/MWRRET/MWRRET_tutorial.ipynb&branch=main) + +* [mwrret1liljclou.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/MWRRET/mwrret1liljclou.c1.ipynb&branch=main) + +* [mwrret1liljclou.c2](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/MWRRET/mwrret1liljclou.c2.ipynb&branch=main) + + +
+ + +
+ MWRRETV2 + +* [MWRRETV2 tutorial](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/MWRRETV2/MWRRETV2_tutorial.ipynb&branch=main) + +* [mwrret2turn.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/MWRRETV2/mwrret2turn.c1.ipynb&branch=main) + + +
+ + +
+ NAVMET-AIR + +* [NAVMET-AIR tutorial](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/NAVMET-AIR/NAVMET-AIR_tutorial.ipynb&branch=main) + +* [aafnaviwg.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/NAVMET-AIR/aafnaviwg.c1.ipynb&branch=main) + + +
+ + +
+ NDROP + +* [NDROP tutorial](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/NDROP/NDROP_tutorial.ipynb&branch=main) + +* [ndropmfrsr.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/NDROP/ndropmfrsr.c1.ipynb&branch=main) + + +
+ + +
+ NEPHELOMETER + +* [NEPHELOMETER tutorial](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/NEPHELOMETER/NEPHELOMETER_tutorial.ipynb&branch=main) + +* [aosnephdry.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/NEPHELOMETER/aosnephdry.c1.ipynb&branch=main) + +* [aosnephwet.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/NEPHELOMETER/aosnephwet.c1.ipynb&branch=main) + + +
+ + +
+ OKMSOIL + +* [OKMSOIL tutorial](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/OKMSOIL/OKMSOIL_tutorial.ipynb&branch=main) + +* [okmsoil.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/OKMSOIL/okmsoil.c1.ipynb&branch=main) + + +
+ + +
+ OZONE-AIR + +* [OZONE-AIR tutorial](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/OZONE-AIR/OZONE-AIR_tutorial.ipynb&branch=main) + +* [aafo3.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/OZONE-AIR/aafo3.c1.ipynb&branch=main) + + +
+ + +
+ PBLHT + +* [PBLHT tutorial](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/PBLHT/PBLHT_tutorial.ipynb&branch=main) + +* [pblhtsonde1mcfarl.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/PBLHT/pblhtsonde1mcfarl.c1.ipynb&branch=main) + +* [pblhtsondeyr1mcfarl.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/PBLHT/pblhtsondeyr1mcfarl.c1.ipynb&branch=main) + + +
+ + +
+ PCCP + +* [PCCP tutorial](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/PCCP/PCCP_tutorial.ipynb&branch=main) + +* [pccp.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/PCCP/pccp.c1.ipynb&branch=main) + + +
+ + +
+ PSAP + +* [PSAP tutorial](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/PSAP/PSAP_tutorial.ipynb&branch=main) + +* [aospsap3w.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/PSAP/aospsap3w.c1.ipynb&branch=main) + + +
+ + +
+ QCRAD + +* [QCRAD tutorial](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/QCRAD/QCRAD_tutorial.ipynb&branch=main) + +* [qcrad1long.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/QCRAD/qcrad1long.c1.ipynb&branch=main) + +* [qcrad1long.c2](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/QCRAD/qcrad1long.c2.ipynb&branch=main) + +* [qcradbeflux1long.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/QCRAD/qcradbeflux1long.c1.ipynb&branch=main) + +* [qcradbeflux1long.c2](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/QCRAD/qcradbeflux1long.c2.ipynb&branch=main) + +* [qcradbrs1long.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/QCRAD/qcradbrs1long.c1.ipynb&branch=main) + +* [qcradbrs1long.c2](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/QCRAD/qcradbrs1long.c2.ipynb&branch=main) + + +
+ + +
+ RADFLUXANAL + +* [RADFLUXANAL tutorial](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/RADFLUXANAL/RADFLUXANAL_tutorial.ipynb&branch=main) + +* [radflux1long.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/RADFLUXANAL/radflux1long.c1.ipynb&branch=main) + +* [radflux1long.c2](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/RADFLUXANAL/radflux1long.c2.ipynb&branch=main) + +* [radfluxbrs1long.c2](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/RADFLUXANAL/radfluxbrs1long.c2.ipynb&branch=main) + + +
+ + +
+ RIPBE + +* [RIPBE tutorial](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/RIPBE/RIPBE_tutorial.ipynb&branch=main) + +* [30ripbe1mcfarlane.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/RIPBE/30ripbe1mcfarlane.c1.ipynb&branch=main) + +* [ripbe1mcfarlane.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/RIPBE/ripbe1mcfarlane.c1.ipynb&branch=main) + + +
+ + +
+ RLPROF + +* [RLPROF tutorial](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/RLPROF/RLPROF_tutorial.ipynb&branch=main) + +* [10rlprofbe1news.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/RLPROF/10rlprofbe1news.c1.ipynb&branch=main) + + +
+ + +
+ SACRADV3D3C + +* [SACRADV3D3C tutorial](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/SACRADV3D3C/SACRADV3D3C_tutorial.ipynb&branch=main) + +* [kasacradv3d3c.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/SACRADV3D3C/kasacradv3d3c.c1.ipynb&branch=main) + + +
+ + +
+ SACRADVVAD + +* [SACRADVVAD tutorial](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/SACRADVVAD/SACRADVVAD_tutorial.ipynb&branch=main) + +* [kasacradvvad.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/SACRADVVAD/kasacradvvad.c1.ipynb&branch=main) + + +
+ + +
+ SFCCLDGRID + +* [SFCCLDGRID tutorial](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/SFCCLDGRID/SFCCLDGRID_tutorial.ipynb&branch=main) + +* [15swfcldgrid1long.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/SFCCLDGRID/15swfcldgrid1long.c1.ipynb&branch=main) + +* [sfccldgrid2longcaracena.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/SFCCLDGRID/sfccldgrid2longcaracena.c1.ipynb&branch=main) + +* [sfccldgrid2longstation.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/SFCCLDGRID/sfccldgrid2longstation.c1.ipynb&branch=main) + + +
+ + +
+ SHALLOWCUMULUS + +* [SHALLOWCUMULUS tutorial](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/SHALLOWCUMULUS/SHALLOWCUMULUS_tutorial.ipynb&branch=main) + +* [shallowcumulus.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/SHALLOWCUMULUS/shallowcumulus.c1.ipynb&branch=main) + +* [shcusummary.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/SHALLOWCUMULUS/shcusummary.c1.ipynb&branch=main) + + +
+ + +
+ SO2-AIR + +* [SO2-AIR tutorial](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/SO2-AIR/SO2-AIR_tutorial.ipynb&branch=main) + +* [aafso2.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/SO2-AIR/aafso2.c1.ipynb&branch=main) + + +
+ + +
+ SONDEADJUST + +* [SONDEADJUST tutorial](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/SONDEADJUST/SONDEADJUST_tutorial.ipynb&branch=main) + +* [sondeadjust.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/SONDEADJUST/sondeadjust.c1.ipynb&branch=main) + + +
+ + +
+ SONDEPARAM + +* [SONDEPARAM tutorial](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/SONDEPARAM/SONDEPARAM_tutorial.ipynb&branch=main) + +* [sondeparam.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/SONDEPARAM/sondeparam.c1.ipynb&branch=main) + + +
+ + +
+ SP2-AIR + +* [SP2-AIR tutorial](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/SP2-AIR/SP2-AIR_tutorial.ipynb&branch=main) + +* [aafsp2rbc10s.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/SP2-AIR/aafsp2rbc10s.c1.ipynb&branch=main) + + +
+ + +
+ SPHOTCOD + +* [SPHOTCOD tutorial](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/SPHOTCOD/SPHOTCOD_tutorial.ipynb&branch=main) + +* [sphotcod2chiu.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/SPHOTCOD/sphotcod2chiu.c1.ipynb&branch=main) + + +
+ + +
+ SURFSPECALB + +* [SURFSPECALB tutorial](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/SURFSPECALB/SURFSPECALB_tutorial.ipynb&branch=main) + +* [surfspecalb1mlawer.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/SURFSPECALB/surfspecalb1mlawer.c1.ipynb&branch=main) + +* [surfspecalb7nch1mlawer.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/SURFSPECALB/surfspecalb7nch1mlawer.c1.ipynb&branch=main) + + +
+ + +
+ TBSMERGED + +* [TBSMERGED tutorial](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/TBSMERGED/TBSMERGED_tutorial.ipynb&branch=main) + +* [tbsmerged.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/TBSMERGED/tbsmerged.c1.ipynb&branch=main) + +* [tbsmergedincloud.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/TBSMERGED/tbsmergedincloud.c1.ipynb&branch=main) + + +
+ + +
+ TDMA + +* [TDMA tutorial](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/TDMA/TDMA_tutorial.ipynb&branch=main) + +* [tdmaapssize.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/TDMA/tdmaapssize.c1.ipynb&branch=main) + + +
+ + +
+ TWRMR + +* [TWRMR tutorial](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/TWRMR/TWRMR_tutorial.ipynb&branch=main) + +* [1twrmr.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/TWRMR/1twrmr.c1.ipynb&branch=main) + +* [30twrmr.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/TWRMR/30twrmr.c1.ipynb&branch=main) + + +
+ + +
+ VARANAL + +* [VARANAL tutorial](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/VARANAL/VARANAL_tutorial.ipynb&branch=main) + +* [180varanaecmwf.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/VARANAL/180varanaecmwf.c1.ipynb&branch=main) + +* [180varanamerra001.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/VARANAL/180varanamerra001.c1.ipynb&branch=main) + + +
+ + +
+ VARANAL3D + +* [VARANAL3D tutorial](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/VARANAL3D/VARANAL3D_tutorial.ipynb&branch=main) + +* [180varanal3dera5.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/VARANAL3D/180varanal3dera5.c1.ipynb&branch=main) + +* [180varanal3dncep.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/VARANAL3D/180varanal3dncep.c1.ipynb&branch=main) + + +
+ + +
+ VDISQUANTS + +* [VDISQUANTS tutorial](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/VDISQUANTS/VDISQUANTS_tutorial.ipynb&branch=main) + +* [vdisquants.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/VDISQUANTS/vdisquants.c1.ipynb&branch=main) + + +
+ + +
+ WACRARSCL + +* [WACRARSCL tutorial](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/WACRARSCL/WACRARSCL_tutorial.ipynb&branch=main) + +* [arsclwacr1kollias.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/WACRARSCL/arsclwacr1kollias.c1.ipynb&branch=main) + +* [arsclwacrbnd1kollias.c1](https://jupyterhub.arm.gov/hub/user-redirect/git-pull?repo=https%3A//github.com/ARM-Development/ARM-Notebooks&urlpath=lab/tree/ARM-Notebooks/../user-data-home/ARM-Notebooks/VAPs/quicklook/WACRARSCL/arsclwacrbnd1kollias.c1.ipynb&branch=main) + + +
+ + + diff --git a/_config.yml b/_config.yml index a6978ab6..92893b03 100644 --- a/_config.yml +++ b/_config.yml @@ -13,3 +13,4 @@ repository: launch_buttons: jupyterhub_url: "https://jupyterhub.arm.gov" notebook_interface: "jupyterlab" +exclude_patterns: ["VAPs/quicklook/*"] # exclude ipynb files to build \ No newline at end of file diff --git a/_toc.yml b/_toc.yml index a54de287..4d978202 100644 --- a/_toc.yml +++ b/_toc.yml @@ -14,6 +14,7 @@ parts: - file: VAPs/README.md sections: - file: VAPs/squire/intro-to-squire.ipynb + - file: VAPs/vap_notebook_list.md - caption: ARM/ASR PI Meeting 2023 chapters: - file: Tutorials/arm-asr-pi-meeting-2023/README.md From 437bbf6ef6e135117545d507ee9da69833b0b339 Mon Sep 17 00:00:00 2001 From: mgrover1 Date: Fri, 10 May 2024 10:53:04 -0500 Subject: [PATCH 2/2] DEL: Remove the checkpoints --- .gitignore | 5 +- .../aaf2dsh.c1-checkpoint.ipynb | 1271 --- .../aaf2dsv.c1-checkpoint.ipynb | 1798 ---- .../aoppsap1flynn1m.c1-checkpoint.ipynb | 8265 ----------------- .../arscl1cloth.c1-checkpoint.ipynb | 2631 ------ .../arsclbnd1cloth.c1-checkpoint.ipynb | 1937 ---- .../bbhrpavg1mlawer.c1-checkpoint.ipynb | 3768 -------- .../rlccnprof1ghan.c1-checkpoint.ipynb | 4109 -------- .../cmac2.c1-checkpoint.ipynb | 3537 ------- .../kazrcorge.c1-checkpoint.ipynb | 445 - .../kazrcorhi.c1-checkpoint.ipynb | 1856 ---- .../kazrcormd.c1-checkpoint.ipynb | 2667 ------ .../mfrsrcldod1min.c1-checkpoint.ipynb | 799 -- .../microbasepi2.c1-checkpoint.ipynb | 468 - .../microbasepiavg.c1-checkpoint.ipynb | 1757 ---- .../mplnor1camp.c1-checkpoint.ipynb | 1732 ---- .../okmsoil.c1-checkpoint.ipynb | 2048 ---- .../pblhtsonde1mcfarl.c1-checkpoint.ipynb | 679 -- .../aospsap3w.c1-checkpoint.ipynb | 1841 ---- .../radflux1long.c1-checkpoint.ipynb | 3763 -------- .../kasacradv3d3c.c1-checkpoint.ipynb | 2574 ----- .../15swfcldgrid1long.c1-checkpoint.ipynb | 2384 ----- ...fccldgrid2longcaracena.c1-checkpoint.ipynb | 5150 ---------- .../SONDEADJUST_tutorial-checkpoint.ipynb | 4058 -------- .../1swfanalsirs1long.c1-checkpoint.ipynb | 2654 ------ 25 files changed, 4 insertions(+), 62192 deletions(-) delete mode 100644 VAPs/quicklook/2DS-AIR/.ipynb_checkpoints/aaf2dsh.c1-checkpoint.ipynb delete mode 100644 VAPs/quicklook/2DS-AIR/.ipynb_checkpoints/aaf2dsv.c1-checkpoint.ipynb delete mode 100644 VAPs/quicklook/AOP/.ipynb_checkpoints/aoppsap1flynn1m.c1-checkpoint.ipynb delete mode 100644 VAPs/quicklook/ARSCL/.ipynb_checkpoints/arscl1cloth.c1-checkpoint.ipynb delete mode 100644 VAPs/quicklook/ARSCL/.ipynb_checkpoints/arsclbnd1cloth.c1-checkpoint.ipynb delete mode 100644 VAPs/quicklook/BBHRP/.ipynb_checkpoints/bbhrpavg1mlawer.c1-checkpoint.ipynb delete mode 100644 VAPs/quicklook/CCNPROF/.ipynb_checkpoints/rlccnprof1ghan.c1-checkpoint.ipynb delete mode 100644 VAPs/quicklook/CMAC2/.ipynb_checkpoints/cmac2.c1-checkpoint.ipynb delete mode 100644 VAPs/quicklook/KAZRCOR/.ipynb_checkpoints/kazrcorge.c1-checkpoint.ipynb delete mode 100644 VAPs/quicklook/KAZRCOR/.ipynb_checkpoints/kazrcorhi.c1-checkpoint.ipynb delete mode 100644 VAPs/quicklook/KAZRCOR/.ipynb_checkpoints/kazrcormd.c1-checkpoint.ipynb delete mode 100644 VAPs/quicklook/MFRSRCLDOD/.ipynb_checkpoints/mfrsrcldod1min.c1-checkpoint.ipynb delete mode 100644 VAPs/quicklook/MICROBASE/.ipynb_checkpoints/microbasepi2.c1-checkpoint.ipynb delete mode 100644 VAPs/quicklook/MICROBASE/.ipynb_checkpoints/microbasepiavg.c1-checkpoint.ipynb delete mode 100644 VAPs/quicklook/MPLNOR/.ipynb_checkpoints/mplnor1camp.c1-checkpoint.ipynb delete mode 100644 VAPs/quicklook/OKMSOIL/.ipynb_checkpoints/okmsoil.c1-checkpoint.ipynb delete mode 100644 VAPs/quicklook/PBLHT/.ipynb_checkpoints/pblhtsonde1mcfarl.c1-checkpoint.ipynb delete mode 100644 VAPs/quicklook/PSAP/.ipynb_checkpoints/aospsap3w.c1-checkpoint.ipynb delete mode 100644 VAPs/quicklook/RADFLUXANAL/.ipynb_checkpoints/radflux1long.c1-checkpoint.ipynb delete mode 100644 VAPs/quicklook/SACRADV3D3C/.ipynb_checkpoints/kasacradv3d3c.c1-checkpoint.ipynb delete mode 100644 VAPs/quicklook/SFCCLDGRID/.ipynb_checkpoints/15swfcldgrid1long.c1-checkpoint.ipynb delete mode 100644 VAPs/quicklook/SFCCLDGRID/.ipynb_checkpoints/sfccldgrid2longcaracena.c1-checkpoint.ipynb delete mode 100644 VAPs/quicklook/SONDEADJUST/.ipynb_checkpoints/SONDEADJUST_tutorial-checkpoint.ipynb delete mode 100644 VAPs/quicklook/SWFLUXANAL/.ipynb_checkpoints/1swfanalsirs1long.c1-checkpoint.ipynb diff --git a/.gitignore b/.gitignore index c71e30af..742b8373 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,5 @@ .DS_Store -_build/ \ No newline at end of file +_build/ + +# Jupyter Notebook +.ipynb_checkpoints diff --git a/VAPs/quicklook/2DS-AIR/.ipynb_checkpoints/aaf2dsh.c1-checkpoint.ipynb b/VAPs/quicklook/2DS-AIR/.ipynb_checkpoints/aaf2dsh.c1-checkpoint.ipynb deleted file mode 100644 index 66d434d8..00000000 --- a/VAPs/quicklook/2DS-AIR/.ipynb_checkpoints/aaf2dsh.c1-checkpoint.ipynb +++ /dev/null @@ -1,1271 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "70840257-70e4-45e2-b491-14bff5a257a3", - "metadata": {}, - "source": [ - "# AAF2DSH.C1 Plots\n", - "\n", - "[Click here](https://www.arm.gov/capabilities/vaps/2ds-air) for more information about this vap." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "460fd89f-e034-452c-b837-f65c5958264f", - "metadata": {}, - "outputs": [], - "source": [ - "%matplotlib widget\n", - "import ipywidgets as widgets\n", - "\n", - "import matplotlib.pyplot as plt\n", - "import ipywidgets as widgets\n", - "import numpy as np\n", - "import pandas as pd\n", - "import os\n", - "from datetime import datetime\n", - "\n", - "import act\n", - "import xarray as xr\n", - "\n", - "# Data archive directory\n", - "DATA_DIR = r'/data/archive/'\n", - "\n", - "# Datastream info\n", - "DATASTREAM_NAME = 'aaf2dsh'\n", - "DATA_LEVEL = 'c1'\n", - "LOCATIONS = [{'end_date': '2018-12-08', 'facility': 'F1', 'site': 'cor', 'start_date': '2018-11-04'}, {'end_date': '2018-02-19', 'facility': 'F1', 'site': 'ena', 'start_date': '2017-06-21'}, {'end_date': '2016-09-22', 'facility': 'F1', 'site': 'sgp', 'start_date': '2016-04-25'}]" - ] - }, - { - "cell_type": "markdown", - "id": "9faaf875", - "metadata": {}, - "source": [ - "## Define site, facility, and date range" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "ac6764f5", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "The following locations and date ranges are available for this VAP:\n" - ] - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
sitefacilitystart_dateend_date
0corF12018-11-042018-12-08
1enaF12017-06-212018-02-19
2sgpF12016-04-252016-09-22
\n", - "
" - ], - "text/plain": [ - " site facility start_date end_date\n", - "0 cor F1 2018-11-04 2018-12-08\n", - "1 ena F1 2017-06-21 2018-02-19\n", - "2 sgp F1 2016-04-25 2016-09-22" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "print(\"The following locations and date ranges are available for this VAP:\")\n", - "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" - ] - }, - { - "cell_type": "markdown", - "id": "8d132223", - "metadata": {}, - "source": [ - "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "e563983a", - "metadata": {}, - "outputs": [], - "source": [ - "site_facility = ( 'sgp', 'F1' )\n", - "\n", - "date_start = '2016-09-21'\n", - "date_end = '2016-09-22'" - ] - }, - { - "cell_type": "markdown", - "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", - "metadata": {}, - "source": [ - "## Load data files\n", - "Load data files from /data/archive/" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'/data/archive/sgp/sgpaaf2dshF1.c1'" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Compile list of files\n", - "site, facility = site_facility\n", - "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", - "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", - "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", - "dir_path\n" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "6be8f3dc", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "['20160921', '20160922']" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "from datetime import date, timedelta\n", - "import pandas as pd\n", - "\n", - "def get_ARM_formated_dates(start_date, end_date):\n", - " \"\"\"\n", - " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", - " EXAMPLE:\n", - " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", - " >> [\"20180219\", \"20180220\", \"20180221\"] \n", - " \"\"\"\n", - " \n", - " _start_date = pd.to_datetime(start_date)\n", - " _end_date = pd.to_datetime(end_date)\n", - " \n", - " delta = _end_date - _start_date # returns timedelta \n", - " dates = []\n", - "\n", - " for i in range(delta.days + 1):\n", - " day = _start_date + timedelta(days=i)\n", - " day_formated = day.strftime(format=\"%Y%m%d\")\n", - " dates.append(day_formated)\n", - " return dates\n", - "\n", - "\n", - "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "51feea2e", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "['/data/archive/sgp/sgpaaf2dshF1.c1/sgpaaf2dshF1.c1.20160921.163940.nc',\n", - " '/data/archive/sgp/sgpaaf2dshF1.c1/sgpaaf2dshF1.c1.20160922.160625.nc']" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Filter a list of files based on date pattern\n", - "import glob\n", - "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", - "files_filter = []\n", - "for date in dates:\n", - " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", - " files_filter\n", - "files_filter" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "id": "b0e5d0d7", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "2 files loaded\n" - ] - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "
<xarray.Dataset>\n",
-       "Dimensions:                     (time: 18380, optical_diameter: 61, bound: 2)\n",
-       "Coordinates:\n",
-       "  * time                        (time) datetime64[ns] 2016-09-21T16:39:40 ......\n",
-       "  * optical_diameter            (optical_diameter) float32 10.0 20.0 ... inf\n",
-       "Dimensions without coordinates: bound\n",
-       "Data variables:\n",
-       "    base_time                   (time) datetime64[ns] 2016-09-21 ... 2016-09-22\n",
-       "    time_offset                 (time) datetime64[ns] 2016-09-21T16:39:40 ......\n",
-       "    optical_diameter_bounds     (time, optical_diameter, bound) float32 dask.array<chunksize=(8283, 61, 2), meta=np.ndarray>\n",
-       "    total_number_concentration  (time) float32 dask.array<chunksize=(8283,), meta=np.ndarray>\n",
-       "    number_concentration        (time, optical_diameter) float32 dask.array<chunksize=(8283, 61), meta=np.ndarray>\n",
-       "    lat                         (time) float32 dask.array<chunksize=(8283,), meta=np.ndarray>\n",
-       "    lon                         (time) float32 dask.array<chunksize=(8283,), meta=np.ndarray>\n",
-       "    alt                         (time) float32 dask.array<chunksize=(8283,), meta=np.ndarray>\n",
-       "Attributes: (12/17)\n",
-       "    command_line:          aaf2dsme_ingest -s sgp -f F1 -D -R\n",
-       "    Conventions:           ARM-1.3\n",
-       "    process_version:       ingest-aaf2dsme-1.2-0.el7\n",
-       "    dod_version:           aaf2dsh-c1-1.1\n",
-       "    input_source:          /data/project/ENG0004504/collection/sgp/sgpaaf2dsF...\n",
-       "    site_id:               sgp\n",
-       "    ...                    ...\n",
-       "    doi:                   10.5439/1419322\n",
-       "    history:               created by user burk on machine prod-proc5.adc.arm...\n",
-       "    _file_dates:           ['20160921', '20160922']\n",
-       "    _file_times:           ['163940', '160625']\n",
-       "    _datastream:           sgpaaf2dshF1.c1\n",
-       "    _arm_standards_flag:   1
" - ], - "text/plain": [ - "\n", - "Dimensions: (time: 18380, optical_diameter: 61, bound: 2)\n", - "Coordinates:\n", - " * time (time) datetime64[ns] 2016-09-21T16:39:40 ......\n", - " * optical_diameter (optical_diameter) float32 10.0 20.0 ... inf\n", - "Dimensions without coordinates: bound\n", - "Data variables:\n", - " base_time (time) datetime64[ns] 2016-09-21 ... 2016-09-22\n", - " time_offset (time) datetime64[ns] 2016-09-21T16:39:40 ......\n", - " optical_diameter_bounds (time, optical_diameter, bound) float32 dask.array\n", - " total_number_concentration (time) float32 dask.array\n", - " number_concentration (time, optical_diameter) float32 dask.array\n", - " lat (time) float32 dask.array\n", - " lon (time) float32 dask.array\n", - " alt (time) float32 dask.array\n", - "Attributes: (12/17)\n", - " command_line: aaf2dsme_ingest -s sgp -f F1 -D -R\n", - " Conventions: ARM-1.3\n", - " process_version: ingest-aaf2dsme-1.2-0.el7\n", - " dod_version: aaf2dsh-c1-1.1\n", - " input_source: /data/project/ENG0004504/collection/sgp/sgpaaf2dsF...\n", - " site_id: sgp\n", - " ... ...\n", - " doi: 10.5439/1419322\n", - " history: created by user burk on machine prod-proc5.adc.arm...\n", - " _file_dates: ['20160921', '20160922']\n", - " _file_times: ['163940', '160625']\n", - " _datastream: sgpaaf2dshF1.c1\n", - " _arm_standards_flag: 1" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Load files as a single dataset\n", - "files_list = files_filter \n", - "ds = act.io.armfiles.read_netcdf(files_list)\n", - "ds.clean.cleanup()\n", - "print(f'{len(files_list)} files loaded')\n", - "ds\n" - ] - }, - { - "cell_type": "markdown", - "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", - "metadata": {}, - "source": [ - "## Plot time series data\n", - "#### Define the list of variables to be plotted:" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "id": "d09b789e-84f1-4605-846b-a72c110c8048", - "metadata": {}, - "outputs": [], - "source": [ - "variables_to_plot = ['total_number_concentration', 'number_concentration']" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", - "metadata": {}, - "outputs": [ - { - "ename": "ValueError", - "evalue": "x and y arguments to pcolormesh cannot have non-finite values or be of type numpy.ma.core.MaskedArray with masked values", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", - "Cell \u001b[0;32mIn[9], line 5\u001b[0m\n\u001b[1;32m 2\u001b[0m ts_display\u001b[38;5;241m.\u001b[39madd_subplots((\u001b[38;5;28mlen\u001b[39m(variables_to_plot),), figsize \u001b[38;5;241m=\u001b[39m (\u001b[38;5;241m9.5\u001b[39m,\u001b[38;5;241m4\u001b[39m\u001b[38;5;241m*\u001b[39m\u001b[38;5;28mlen\u001b[39m(variables_to_plot)))\n\u001b[1;32m 4\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m i,v \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28menumerate\u001b[39m(variables_to_plot):\n\u001b[0;32m----> 5\u001b[0m ts_ax \u001b[38;5;241m=\u001b[39m \u001b[43mts_display\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mplot\u001b[49m\u001b[43m(\u001b[49m\u001b[43mv\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43msubplot_index\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mi\u001b[49m\u001b[43m,\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mset_title\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mds\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mvariables\u001b[49m\u001b[43m[\u001b[49m\u001b[43mv\u001b[49m\u001b[43m]\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mattrs\u001b[49m\u001b[43m[\u001b[49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43mlong_name\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m,\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 6\u001b[0m ts_ax\u001b[38;5;241m.\u001b[39mgrid()\n\u001b[1;32m 8\u001b[0m plt\u001b[38;5;241m.\u001b[39mshow()\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/act/plotting/timeseriesdisplay.py:588\u001b[0m, in \u001b[0;36mTimeSeriesDisplay.plot\u001b[0;34m(self, field, dsname, subplot_index, cmap, set_title, add_nan, day_night_background, invert_y_axis, abs_limits, time_rng, y_rng, use_var_for_y, set_shading, assessment_overplot, overplot_marker, overplot_behind, overplot_markersize, assessment_overplot_category, assessment_overplot_category_color, force_line_plot, labels, cbar_label, cbar_h_adjust, secondary_y, y_axis_flag_meanings, colorbar_labels, cb_friendly, **kwargs)\u001b[0m\n\u001b[1;32m 586\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124medgecolors\u001b[39m\u001b[38;5;124m'\u001b[39m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;129;01min\u001b[39;00m kwargs\u001b[38;5;241m.\u001b[39mkeys():\n\u001b[1;32m 587\u001b[0m kwargs[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124medgecolors\u001b[39m\u001b[38;5;124m'\u001b[39m] \u001b[38;5;241m=\u001b[39m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mface\u001b[39m\u001b[38;5;124m'\u001b[39m\n\u001b[0;32m--> 588\u001b[0m mesh \u001b[38;5;241m=\u001b[39m \u001b[43max\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mpcolormesh\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 589\u001b[0m \u001b[43m \u001b[49m\u001b[43mnp\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43masarray\u001b[49m\u001b[43m(\u001b[49m\u001b[43mxdata\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 590\u001b[0m \u001b[43m \u001b[49m\u001b[43mydata\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 591\u001b[0m \u001b[43m \u001b[49m\u001b[43mdata\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mtranspose\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 592\u001b[0m \u001b[43m \u001b[49m\u001b[43mshading\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mset_shading\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 593\u001b[0m \u001b[43m \u001b[49m\u001b[43mcmap\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcmap\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 594\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 595\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 597\u001b[0m \u001b[38;5;66;03m# Set Title\u001b[39;00m\n\u001b[1;32m 598\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m set_title \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/matplotlib/__init__.py:1442\u001b[0m, in \u001b[0;36m_preprocess_data..inner\u001b[0;34m(ax, data, *args, **kwargs)\u001b[0m\n\u001b[1;32m 1439\u001b[0m \u001b[38;5;129m@functools\u001b[39m\u001b[38;5;241m.\u001b[39mwraps(func)\n\u001b[1;32m 1440\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21minner\u001b[39m(ax, \u001b[38;5;241m*\u001b[39margs, data\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mNone\u001b[39;00m, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs):\n\u001b[1;32m 1441\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m data \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[0;32m-> 1442\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mfunc\u001b[49m\u001b[43m(\u001b[49m\u001b[43max\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;28;43mmap\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43msanitize_sequence\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43margs\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 1444\u001b[0m bound \u001b[38;5;241m=\u001b[39m new_sig\u001b[38;5;241m.\u001b[39mbind(ax, \u001b[38;5;241m*\u001b[39margs, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs)\n\u001b[1;32m 1445\u001b[0m auto_label \u001b[38;5;241m=\u001b[39m (bound\u001b[38;5;241m.\u001b[39marguments\u001b[38;5;241m.\u001b[39mget(label_namer)\n\u001b[1;32m 1446\u001b[0m \u001b[38;5;129;01mor\u001b[39;00m bound\u001b[38;5;241m.\u001b[39mkwargs\u001b[38;5;241m.\u001b[39mget(label_namer))\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/matplotlib/axes/_axes.py:6220\u001b[0m, in \u001b[0;36mAxes.pcolormesh\u001b[0;34m(self, alpha, norm, cmap, vmin, vmax, shading, antialiased, *args, **kwargs)\u001b[0m\n\u001b[1;32m 6217\u001b[0m shading \u001b[38;5;241m=\u001b[39m shading\u001b[38;5;241m.\u001b[39mlower()\n\u001b[1;32m 6218\u001b[0m kwargs\u001b[38;5;241m.\u001b[39msetdefault(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124medgecolors\u001b[39m\u001b[38;5;124m'\u001b[39m, \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mnone\u001b[39m\u001b[38;5;124m'\u001b[39m)\n\u001b[0;32m-> 6220\u001b[0m X, Y, C, shading \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_pcolorargs\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43mpcolormesh\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 6221\u001b[0m \u001b[43m \u001b[49m\u001b[43mshading\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mshading\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mkwargs\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 6222\u001b[0m coords \u001b[38;5;241m=\u001b[39m np\u001b[38;5;241m.\u001b[39mstack([X, Y], axis\u001b[38;5;241m=\u001b[39m\u001b[38;5;241m-\u001b[39m\u001b[38;5;241m1\u001b[39m)\n\u001b[1;32m 6223\u001b[0m \u001b[38;5;66;03m# convert to one dimensional array, except for 3D RGB(A) arrays\u001b[39;00m\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/matplotlib/axes/_axes.py:5717\u001b[0m, in \u001b[0;36mAxes._pcolorargs\u001b[0;34m(self, funcname, shading, *args, **kwargs)\u001b[0m\n\u001b[1;32m 5715\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m funcname \u001b[38;5;241m==\u001b[39m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mpcolormesh\u001b[39m\u001b[38;5;124m'\u001b[39m:\n\u001b[1;32m 5716\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m np\u001b[38;5;241m.\u001b[39mma\u001b[38;5;241m.\u001b[39mis_masked(X) \u001b[38;5;129;01mor\u001b[39;00m np\u001b[38;5;241m.\u001b[39mma\u001b[38;5;241m.\u001b[39mis_masked(Y):\n\u001b[0;32m-> 5717\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\n\u001b[1;32m 5718\u001b[0m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mx and y arguments to pcolormesh cannot have \u001b[39m\u001b[38;5;124m'\u001b[39m\n\u001b[1;32m 5719\u001b[0m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mnon-finite values or be of type \u001b[39m\u001b[38;5;124m'\u001b[39m\n\u001b[1;32m 5720\u001b[0m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mnumpy.ma.core.MaskedArray with masked values\u001b[39m\u001b[38;5;124m'\u001b[39m)\n\u001b[1;32m 5721\u001b[0m \u001b[38;5;66;03m# safe_masked_invalid() returns an ndarray for dtypes other\u001b[39;00m\n\u001b[1;32m 5722\u001b[0m \u001b[38;5;66;03m# than floating point.\u001b[39;00m\n\u001b[1;32m 5723\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(X, np\u001b[38;5;241m.\u001b[39mma\u001b[38;5;241m.\u001b[39mcore\u001b[38;5;241m.\u001b[39mMaskedArray):\n", - "\u001b[0;31mValueError\u001b[0m: x and y arguments to pcolormesh cannot have non-finite values or be of type numpy.ma.core.MaskedArray with masked values" - ] - }, - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "eae83e9e4798482083ecdb12aeb73cf7", - "version_major": 2, - "version_minor": 0 - }, - "image/png": "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ufvs53B4F8G2hjp27JgkKTEx0ccjAQAAAC4fx44dk91u9/UwLjs2w58UaqSysjLt27dPERERstlsvh6OpDN/xUpMTNSePXsUGRnp6+FYHvV0P2rqXtTTvaine1FP96Ke7kU93ctb9TTG6NixY0pISFCtWnzj09s4YltD1apVS/Xr1/f1MCoUGRnJL2k3op7uR03di3q6F/V0L+rpXtTTvaine3mjnhyp9R3+lAAAAAAAsDSCLQAAAADA0gi28JqQkBCNGzdOISEhvh5KjUA93Y+auhf1dC/q6V7U072op3tRT/einpcHLh4FAAAAALA0jtgCAAAAACyNYAsAAAAAsDSCLQAAAADA0gi2AAAAAABLI9iiWj7//HPde++9SkhIkM1m0wcffODy/PHjx/Xkk0+qfv36CgsLU9OmTTVz5swqt7t582a1a9dOYWFhqlevniZMmKBzr2e2cuVKpaSkKDQ0VFdffbVef/11d07N6zIyMnTrrbcqIiJCMTExuv/++7Vjxw6XPu+//746d+6s6Oho2Ww2ZWdnV2vbl2M9z5oxY4aSkpIUGhqqlJQUrVq1yvmcMUbp6elKSEhQWFiY2rdvr61bt1a5zcu5ntL5a5qenq7rr79etWvXVp06ddSxY0etX7++ym1ezjU9Xz0lafv27erRo4fsdrsiIiLUunVr5eTknHeb1LPieu7fv18DBgxQQkKCwsPD1aVLF3333XdVbvNyref5/o0vKSnRs88+q2bNmql27dpKSEhQv379tG/fviq3Sz0r/sw0YMAA2Ww2l0fr1q2r3C715DMoqmCAali6dKkZO3asee+994wks3jxYpfnH330UXPNNdeYFStWmJ07d5pZs2aZgIAA88EHH1S6TYfDYWJjY82DDz5oNm/ebN577z0TERFh/vjHPzr7/PjjjyY8PNw8/fTTZtu2bWb27NkmKCjI/P3vf/fUVD2uc+fOZt68eWbLli0mOzvb3HPPPaZBgwbm+PHjzj5vv/22GT9+vJk9e7aRZLKysqrc7uVaT2OMWbhwoQkKCjKzZ88227ZtM08//bSpXbu22b17tzHGmJdeeslERESY9957z2zevNn07t3bxMfHm4KCgkq3eTnX05iqa7pgwQKTmZlpfvjhB7NlyxYzcOBAExkZafLz8yvd5uVc06rq+f3335uoqCjz29/+1nz55Zfmhx9+MB9++KHZv39/pduknhXXs6yszLRu3drccccdZsOGDeabb74xjz32WLnfs+e6nOt5vn/jjx49ajp27GgWLVpkvvnmG7N27VrTqlUrk5KSct5tUs/KPzP179/fdOnSxeTm5jofhw4dOu82qSefQVE1gi0uWEW/VG688UYzYcIEl7ZbbrnF/O53v6t0OzNmzDB2u92cOnXK2ZaRkWESEhJMWVmZMcaYZ555xlx//fUu6z3++OOmdevWlzgL/5Gfn28kmZUrV5Z7bufOndUOtpdzPW+77TYzePBgl7brr7/ejB492pSVlZm4uDjz0ksvOZ87deqUsdvt5vXXX690m5dzPY05f00r4nA4jCTzySefVLrNy7mmVdWzd+/e5te//vUFbZN6VlzPHTt2GElmy5YtzudOnz5toqKizOzZsyvd5uVcz1+q6N/4c23YsMFIcv5hpiLU84zKgu199913QduhnmfwGRTnw6nIcIvbb79dS5Ys0U8//SRjjFasWKFvv/1WnTt3dvYZMGCA2rdv71xeu3at2rVr53Kz7M6dO2vfvn3atWuXs09aWprLvjp37qwvvvhCJSUlHp2TtzgcDklSVFTUBa1HPc8oLi7Wpk2bys0rLS1Na9as0c6dO5WXl+fyfEhIiNq1a6c1a9Y426jnz6qqaUX933jjDdntdrVo0cLZTk3PqKqeZWVl+uijj3Tdddepc+fOiomJUatWrSo8fZF6Vl3PoqIiSVJoaKjzuYCAAAUHB2v16tXONup58RwOh2w2m6688kpnG/W8MJ999pliYmJ03XXXadCgQcrPz3d5nnpWH59BcRbBFm7x5z//WTfccIPq16+v4OBgdenSRTNmzNDtt9/u7BMfH68GDRo4l/Py8hQbG+uynbPLeXl55+1z+vRpHTx40FPT8RpjjEaMGKHbb79dycnJF7Qu9Tzj4MGDKi0trXBeeXl5zrlX9vxZ1PNnVdX0rA8//FBXXHGFQkND9eqrryozM1PR0dHO56npGVXVMz8/X8ePH9dLL72kLl26aPny5XrggQfUs2dPrVy50tmfep5RVT2vv/56NWzYUGPGjNGRI0dUXFysl156SXl5ecrNzXX2p54X59SpUxo9erT69OmjyMhIZzv1rL6uXbtqwYIF+vTTT/XKK69o48aNuuuuu5x/lJGo54XgMyjOCvT1AFAz/PnPf9a6deu0ZMkSNWzYUJ9//rmGDBmi+Ph4dezYUdKZiyady2azuSyb/35p/5ft1eljVU8++aS+/vprl6MI1UU9XVU0r6rm/cs26lleVTXr0KGDsrOzdfDgQc2ePVu9evXS+vXrFRMTI4manquyepaVlUmS7rvvPg0fPlySdNNNN2nNmjV6/fXX1a5dO0nU81yV1TMoKEjvvfeeBg4cqKioKAUEBKhjx47q2rWrS3/qeeFKSkr04IMPqqysTDNmzHB5jnpWX+/evZ3/n5ycrJYtW6phw4b66KOP1LNnT0nU80LwGRRnEWxxyQoLC/Xcc89p8eLFuueeeyRJzZs3V3Z2tv74xz86f6mcKy4uzuXojyTnqThn/0JWWZ/AwEDVrVvX3VPxqqeeekpLlizR559/rvr161/y9i7XekZHRysgIKDCecXGxiouLk7Smb+8xsfHl3u+MpdrPaWqa3pW7dq1de211+raa69V69at1bhxY82ZM0djxoypcLuXa02rqmd0dLQCAwN1ww03uDzftGnT8/7Ri3pW/v5MSUlRdna2HA6HiouLddVVV6lVq1Zq2bJlpdu9XOtZXSUlJerVq5d27typTz/91OVobUWoZ/XFx8erYcOG571yN/WsGJ9B8UucioxLVlJSopKSEtWq5fp2CggIcB6JqEhqaqo+//xzFRcXO9uWL1+uhIQENWrUyNknMzPTZb3ly5erZcuWCgoKct8kvMgYoyeffFLvv/++Pv30UyUlJbllu5drPYODg5WSklJuXpmZmWrTpo2SkpIUFxfn8nxxcbFWrlypNm3aVLrdy7WeUtU1rYwxxuVUunNdrjWtqp7BwcG69dZby93269tvv1XDhg0r3S71rPr9abfbddVVV+m7777TF198ofvuu6/S7V6u9ayOs6H2u+++0yeffFKtD/XUs/oOHTqkPXv2uPzx9VzUs2J8BoUL71yjClZ37Ngxk5WVZbKysowkM2XKFJOVleW8ImK7du3MjTfeaFasWGF+/PFHM2/ePBMaGmpmzJjh3Mbo0aNN3759nctHjx41sbGx5qGHHjKbN28277//vomMjKzwUuvDhw8327ZtM3PmzLH8pdb/93//19jtdvPZZ5+5XOr/5MmTzj6HDh0yWVlZ5qOPPjKSzMKFC01WVpbJzc119qGePzt76485c+aYbdu2mWHDhpnatWubXbt2GWPO3O7H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oOrWrVvhOiEhIQoJCSnXbrfb+QVUDZGRkdSpCtSoeqhT1ahR9VCn6qFOVaNG1UOdqkaNqqdWLa7P6wtU3cOCg4OVkpKizMxMl/bMzEy1adOmwnVSU1PL9V++fLlatmxZ4fdrAQAAAOByRrD1ghEjRugvf/mL5s6dq+3bt2v48OHKycnR4MGDJZ05jbhfv37O/oMHD9bu3bs1YsQIbd++XXPnztWcOXM0atQoX00BAAAAAPwWpyJ7Qe/evXXo0CFNmDBBubm5Sk5O1tKlS9WwYUNJUm5urss9bZOSkrR06VINHz5cr732mhISEvTnP/9Zv/rVr6q9z5CQEI0bN67C05PxM+pUNWpUPdSpatSoeqhT9VCnqlGj6qFOVaNG1UOdfIv72AIAAAAALI1TkQEAAAAAlkawBQAAAABYGsEWAAAAAGBpBFsAAAAAgKURbP1URkaGbr31VkVERCgmJkb333+/duzY4dLHGKP09HQlJCQoLCxM7du319atW136FBUV6amnnlJ0dLRq166tHj16aO/evS59jhw5or59+8put8tut6tv3746evSop6foFt6s04svvqg2bdooPDxcV155paen5jbeqtGuXbs0cOBAJSUlKSwsTNdcc43GjRun4uJir8zzUnnzvdSjRw81aNBAoaGhio+PV9++fbVv3z6Pz/FSebNGv+x70003yWazKTs721NTcytv1qlRo0ay2Wwuj9GjR3t8ju7g7ffTRx99pFatWiksLEzR0dHq2bOnR+fnDt6q0WeffVbufXT2sXHjRq/M9VJ487307bff6r777lN0dLQiIyPVtm1brVixwuNzdAdv1unLL79Up06ddOWVV6pu3bp67LHHdPz4cY/P8VK5q0ZvvPGG2rdvr8jISNlstgo/V1v587ffMvBLnTt3NvPmzTNbtmwx2dnZ5p577jENGjQwx48fd/Z56aWXTEREhHnvvffM5s2bTe/evU18fLwpKChw9hk8eLCpV6+eyczMNF9++aXp0KGDadGihTl9+rSzT5cuXUxycrJZs2aNWbNmjUlOTjbdu3f36nwvljfr9MILL5gpU6aYESNGGLvd7s1pXhJv1ehf//qXGTBggPn444/NDz/8YP7xj3+YmJgYM3LkSK/P+WJ48700ZcoUs3btWrNr1y7zn//8x6SmpprU1FSvzvdieLNGZw0dOtR07drVSDJZWVnemOYl82adGjZsaCZMmGByc3Odj2PHjnl1vhfLm3X6+9//burUqWNmzpxpduzYYb755hvzt7/9zavzvRjeqlFRUZHLeyg3N9c8+uijplGjRqasrMzr875Q3nwvXXvttaZbt27mq6++Mt9++60ZMmSICQ8PN7m5uV6d88XwVp1++uknU6dOHTN48GDzzTffmA0bNpg2bdqYX/3qV16f84VyV41effVVk5GRYTIyMowkc+TIkXL7svLnb39FsLWI/Px8I8msXLnSGGNMWVmZiYuLMy+99JKzz6lTp4zdbjevv/66McaYo0ePmqCgILNw4UJnn59++snUqlXLLFu2zBhjzLZt24wks27dOmeftWvXGknmm2++8cbU3MpTdfqlefPmWSrYnssbNTpr8uTJJikpyUMz8Sxv1ukf//iHsdlspri42EOz8QxP12jp0qXm+uuvN1u3brVUsD2XJ+vUsGFD8+qrr3pnIh7mqTqVlJSYevXqmb/85S9enI1neOv3UnFxsYmJiTETJkzw4Gw8x1N1OnDggJFkPv/8c2efgoICI8l88skn3piaW3mqTrNmzTIxMTGmtLTU2ScrK8tIMt999503puY2F1OjX1qxYkWFwbamff72F5yKbBEOh0OSFBUVJUnauXOn8vLylJaW5uwTEhKidu3aac2aNZKkTZs2qaSkxKVPQkKCkpOTnX3Wrl0ru92uVq1aOfu0bt1adrvd2cdKPFWnmsSbNXI4HM79WI236nT48GEtWLBAbdq0UVBQkKem4xGerNH+/fs1aNAg/fWvf1V4eLg3puMxnn4vTZo0SXXr1tVNN92kF1980TKn/5/LU3X68ssv9dNPP6lWrVq6+eabFR8fr65du5Y7ddAKvPV7acmSJTp48KAGDBjgoZl4lqfqVLduXTVt2lRvv/22Tpw4odOnT2vWrFmKjY1VSkqKt6bnNp6qU1FRkYKDg1Wr1s8xIywsTJK0evVqz07KzS6mRtVR0z5/+wuCrQUYYzRixAjdfvvtSk5OliTl5eVJkmJjY136xsbGOp/Ly8tTcHCw6tSpc94+MTEx5fYZExPj7GMVnqxTTeHNGv3www+aNm2aBg8e7O5peJw36vTss8+qdu3aqlu3rnJycvSPf/zDU9PxCE/WyBijAQMGaPDgwWrZsqWnp+JRnn4vPf3001q4cKFWrFihJ598UlOnTtWQIUM8OSWP8GSdfvzxR0lSenq6fve73+nDDz9UnTp11K5dOx0+fNij83Inb/7+njNnjjp37qzExER3T8PjPFknm82mzMxMZWVlKSIiQqGhoXr11Ve1bNkyS117Q/Jsne666y7l5eXp5ZdfVnFxsY4cOaLnnntOkpSbm+vRebnTxdaoOmrS529/QrC1gCeffFJff/213n333XLP2Ww2l2VjTLm2c53bp6L+1dmOv/F0nWoCb9Vo37596tKli/7f//t/evTRRy9t0D7gjTr99re/VVZWlpYvX66AgAD169dPxphLH7yXeLJG06ZNU0FBgcaMGeO+AfuIp99Lw4cPV7t27dS8eXM9+uijev311zVnzhwdOnTIPRPwEk/WqaysTJI0duxY/epXv1JKSormzZsnm82mv/3tb26aged56/f33r179fHHH2vgwIGXNmAf8WSdjDEaMmSIYmJitGrVKm3YsEH33XefunfvbqnAJnm2TjfeeKPeeustvfLKKwoPD1dcXJyuvvpqxcbGKiAgwH2T8DB316iqbVzsdvAzgq2fe+qpp7RkyRKtWLFC9evXd7bHxcVJUrm/6uTn5zv/ihQXF+f8S9n5+uzfv7/cfg8cOFDur1H+zNN1qgm8VaN9+/apQ4cOSk1N1RtvvOGJqXiUt+oUHR2t6667Tp06ddLChQu1dOlSrVu3zhNTcjtP1+jTTz/VunXrFBISosDAQF177bWSpJYtW6p///4em5e7+eL3UuvWrSVJ33//vVvm4A2erlN8fLwk6YYbbnA+HxISoquvvlo5OTnun5AHePO9NG/ePNWtW1c9evRw9zQ8zhu/mz788EMtXLhQbdu21S233KIZM2YoLCxMb731lien5lbeeD/16dNHeXl5+umnn3To0CGlp6frwIEDSkpK8tS03OpSalQdNeXzt9/x6Dd4cdHKysrME088YRISEsy3335b4fNxcXFm0qRJzraioqIKv+C/aNEiZ599+/ZVePGo9evXO/usW7fOMl9e91adfslqF4/yZo327t1rGjdubB588MEKr3Drz3zxXjorJyfHSDIrVqxw34Q8wFs12r17t9m8ebPz8fHHHxtJ5u9//7vZs2ePh2d56Xz5XvrnP/9pJJndu3e7cUae4a06ORwOExIS4nLxqLMXR5o1a5anpucW3n4vlZWVmaSkJMtczf4sb9VpyZIlplatWuWuPH7dddeZF1980RNTcytf/m6aM2eOCQ8Pr/DqwP7EHTX6paouHmXVz9/+imDrp/73f//X2O1289lnn7lcfv/kyZPOPi+99JKx2+3m/fffN5s3bzYPPfRQhZdkr1+/vvnkk0/Ml19+ae66664Kb/fTvHlzs3btWrN27VrTrFkzy1xu3Jt12r17t8nKyjLjx483V1xxhcnKyjJZWVl+f2sNb9Xop59+Mtdee6256667zN69e132ZQXeqtP69evNtGnTTFZWltm1a5f59NNPze23326uueYac+rUKa/P+0J48+ftl3bu3GmpqyJ7q05r1qwxU6ZMMVlZWebHH380ixYtMgkJCaZHjx5en/PF8Ob76emnnzb16tUzH3/8sfnmm2/MwIEDTUxMjDl8+LBX53yhvP0z98knnxhJZtu2bV6bozt4q04HDhwwdevWNT179jTZ2dlmx44dZtSoUSYoKMhkZ2d7fd4Xypvvp2nTpplNmzaZHTt2mOnTp5uwsDDzpz/9yavzvRjuqlFubq7Jysoys2fPdl5JOysryxw6dMjZx8qfv/0VwdZPSarwMW/ePGefsrIyM27cOBMXF2dCQkLMnXfeaTZv3uyyncLCQvPkk0+aqKgoExYWZrp3725ycnJc+hw6dMg8/PDDJiIiwkRERJiHH37Y7/+idpY369S/f/8K9+XvR9m8VaN58+ZVui8r8Fadvv76a9OhQwcTFRVlQkJCTKNGjczgwYPN3r17vTXVi+bNn7dfslqw9VadNm3aZFq1amXsdrsJDQ01TZo0MePGjTMnTpzw1lQviTffT8XFxWbkyJEmJibGREREmI4dO5otW7Z4Y5qXxNs/cw899JBp06aNp6fldt6s08aNG01aWpqJiooyERERpnXr1mbp0qXemOYl82ad+vbta6KiokxwcLBp3ry5efvtt70xxUvmrhqNGzeuyu1Y+fO3v7IZY6GrlQAAAAAAcA4uHgUAAAAAsDSCLQAAAADA0gi2AAAAAABLI9gCAAAAACyNYAsAAAAAsDSCLQAAAADA0gi2AAAAAABLI9gCAAAAACyNYAsAAAAAsDSCLQAAAADA0gi2AAAAAABLI9gCAAAAACyNYAsAAAAAsDSCLQAAAADA0gi2AAAAAABLI9gCAAAAACyNYAsAAAAAsDSCLQAAAADA0gi2AAAAAABLI9gCAAAAACyNYAsAAAAAsDSCLQAAAADA0gi2AAAAAABLI9gCAAAAACyNYAsAAAAAsDSCLQAAAADA0gi2AAAAAABLI9gCAAAAACyNYAsAAAAAsDSCLQAAAADA0gi2AAAAAABLI9gCAAAAACyNYAsAAAAAsDSCLQAAAADA0gi2AAAAAABLI9gCAAAAACyNYAsAAAAAsDSCLQAAAADA0gi2AAAAAABLI9gCAAAAACyNYAsAAAAAsDSCLQAAAADA0gi2AAAAAABLI9gCAAAAACyNYAsAAAAAsDSCLQAAAADA0gi2AAAAAABLI9gCAAAAACyNYAsAAAAAsDSCLQAAAADA0gi2AAAAAABLI9gCAAAAACyNYAsAAAAAsDSCLQAAAADA0gi2AAAAAABLI9gCAAAAACyNYAsAAAAAsDSCLQAAAADA0gi2AAAAAABLI9gCAAAAACyNYAsAAAAAsDSCLQAAAADA0gi2AAAAAABLI9gCAAAAACyNYAsAAAAAsDSCLQAAAADA0v4/FN3X2Uk25/AAAAAASUVORK5CYII=", - "text/html": [ - "\n", - "
\n", - "
\n", - " Figure\n", - "
\n", - " \n", - "
\n", - " " - ], - "text/plain": [ - "Canvas(toolbar=Toolbar(toolitems=[('Home', 'Reset original view', 'home', 'home'), ('Back', 'Back to previous …" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", - "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", - "\n", - "for i,v in enumerate(variables_to_plot):\n", - " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", - " ts_ax.grid()\n", - "\n", - "plt.show()\n" - ] - }, - { - "cell_type": "markdown", - "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", - "metadata": {}, - "source": [ - "## Field selection dropdown menu\n", - "Select variable to be plotted from a dropdown menu" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", - "metadata": {}, - "outputs": [], - "source": [ - "plt.ioff()\n", - "\n", - "# populate dropdown menu with available variables \n", - "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", - "d_variable = 'total_number_concentration'\n", - "dropdown = widgets.Dropdown(\n", - " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", - " value= d_variable,\n", - " description='Field:',\n", - " disabled=False,\n", - ")\n", - "dropdown.layout.margin = '0px 30% 0px 20%'\n", - "dropdown.layout.width = '50%'\n", - "\n", - "# set up display\n", - "i_display = act.plotting.TimeSeriesDisplay(ds)\n", - "i_display.add_subplots((1,), figsize = (9.5,5))\n", - "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", - "i_ax.grid()\n", - "i_fig = i_display.fig\n", - "\n", - "# update plot callback function\n", - "def update_plot(change):\n", - " i_ax.cla()\n", - " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", - " i_ax_new.grid()\n", - " i_fig.canvas.draw()\n", - " i_fig.canvas.flush_events()\n", - "\n", - "dropdown.observe(update_plot, names='value')\n", - "\n", - "widgets.AppLayout(\n", - " header=dropdown,\n", - " center=i_fig.canvas,\n", - " pane_heights=[1, 6,1]\n", - ")\n" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.16" - }, - "widgets": { - "application/vnd.jupyter.widget-state+json": { - "state": {}, - "version_major": 2, - "version_minor": 0 - } - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/VAPs/quicklook/2DS-AIR/.ipynb_checkpoints/aaf2dsv.c1-checkpoint.ipynb b/VAPs/quicklook/2DS-AIR/.ipynb_checkpoints/aaf2dsv.c1-checkpoint.ipynb deleted file mode 100644 index 9a856041..00000000 --- a/VAPs/quicklook/2DS-AIR/.ipynb_checkpoints/aaf2dsv.c1-checkpoint.ipynb +++ /dev/null @@ -1,1798 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "70840257-70e4-45e2-b491-14bff5a257a3", - "metadata": {}, - "source": [ - "# AAF2DSV.C1 Plots\n", - "\n", - "[Click here](https://www.arm.gov/capabilities/vaps/2ds-air) for more information about this vap." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "460fd89f-e034-452c-b837-f65c5958264f", - "metadata": {}, - "outputs": [], - "source": [ - "%matplotlib widget\n", - "import ipywidgets as widgets\n", - "\n", - "import matplotlib.pyplot as plt\n", - "import ipywidgets as widgets\n", - "import numpy as np\n", - "import pandas as pd\n", - "import os\n", - "from datetime import datetime\n", - "\n", - "import act\n", - "import xarray as xr\n", - "\n", - "# Data archive directory\n", - "DATA_DIR = r'/data/archive/'\n", - "\n", - "# Datastream info\n", - "DATASTREAM_NAME = 'aaf2dsv'\n", - "DATA_LEVEL = 'c1'\n", - "LOCATIONS = [{'end_date': '2018-12-08', 'facility': 'F1', 'site': 'cor', 'start_date': '2018-11-04'}, {'end_date': '2018-02-19', 'facility': 'F1', 'site': 'ena', 'start_date': '2017-06-21'}, {'end_date': '2016-09-22', 'facility': 'F1', 'site': 'sgp', 'start_date': '2016-04-25'}]" - ] - }, - { - "cell_type": "markdown", - "id": "9faaf875", - "metadata": {}, - "source": [ - "## Define site, facility, and date range" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "ac6764f5", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "The following locations and date ranges are available for this VAP:\n" - ] - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
sitefacilitystart_dateend_date
0corF12018-11-042018-12-08
1enaF12017-06-212018-02-19
2sgpF12016-04-252016-09-22
\n", - "
" - ], - "text/plain": [ - " site facility start_date end_date\n", - "0 cor F1 2018-11-04 2018-12-08\n", - "1 ena F1 2017-06-21 2018-02-19\n", - "2 sgp F1 2016-04-25 2016-09-22" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "print(\"The following locations and date ranges are available for this VAP:\")\n", - "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" - ] - }, - { - "cell_type": "markdown", - "id": "8d132223", - "metadata": {}, - "source": [ - "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "e563983a", - "metadata": {}, - "outputs": [], - "source": [ - "site_facility = ( 'sgp', 'F1' )\n", - "\n", - "date_start = '2016-09-21'\n", - "date_end = '2016-09-22'" - ] - }, - { - "cell_type": "markdown", - "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", - "metadata": {}, - "source": [ - "## Load data files\n", - "Load data files from /data/archive/" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'/data/archive/sgp/sgpaaf2dsvF1.c1'" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Compile list of files\n", - "site, facility = site_facility\n", - "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", - "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", - "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", - "dir_path\n" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "6be8f3dc", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "['20160921', '20160922']" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "from datetime import date, timedelta\n", - "import pandas as pd\n", - "\n", - "def get_ARM_formated_dates(start_date, end_date):\n", - " \"\"\"\n", - " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", - " EXAMPLE:\n", - " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", - " >> [\"20180219\", \"20180220\", \"20180221\"] \n", - " \"\"\"\n", - " \n", - " _start_date = pd.to_datetime(start_date)\n", - " _end_date = pd.to_datetime(end_date)\n", - " \n", - " delta = _end_date - _start_date # returns timedelta \n", - " dates = []\n", - "\n", - " for i in range(delta.days + 1):\n", - " day = _start_date + timedelta(days=i)\n", - " day_formated = day.strftime(format=\"%Y%m%d\")\n", - " dates.append(day_formated)\n", - " return dates\n", - "\n", - "\n", - "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "51feea2e", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "['/data/archive/sgp/sgpaaf2dsvF1.c1/sgpaaf2dsvF1.c1.20160921.163940.nc',\n", - " '/data/archive/sgp/sgpaaf2dsvF1.c1/sgpaaf2dsvF1.c1.20160922.160625.nc']" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Filter a list of files based on date pattern\n", - "import glob\n", - "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", - "files_filter = []\n", - "for date in dates:\n", - " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", - " files_filter\n", - "files_filter" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "id": "226f29ae", - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "
<xarray.Dataset>\n",
-       "Dimensions:                     (time: 10097, optical_diameter: 61, bound: 2)\n",
-       "Coordinates:\n",
-       "  * time                        (time) datetime64[ns] 2016-09-22T16:06:25 ......\n",
-       "  * optical_diameter            (optical_diameter) float32 10.0 20.0 ... inf\n",
-       "Dimensions without coordinates: bound\n",
-       "Data variables:\n",
-       "    base_time                   datetime64[ns] 2016-09-22\n",
-       "    time_offset                 (time) datetime64[ns] 2016-09-22T16:06:25 ......\n",
-       "    optical_diameter_bounds     (optical_diameter, bound) float32 5.0 ... inf\n",
-       "    total_number_concentration  (time) float32 26.59 0.0 22.4 ... 0.0 0.0 nan\n",
-       "    number_concentration        (time, optical_diameter) float32 2.24 ... nan\n",
-       "    lat                         (time) float32 36.76 36.76 36.76 ... 36.74 36.74\n",
-       "    lon                         (time) float32 -96.01 -96.01 ... -96.02 -96.02\n",
-       "    alt                         (time) float32 220.0 224.0 228.0 ... 677.0 674.0\n",
-       "Attributes: (12/13)\n",
-       "    command_line:          aaf2dsme_ingest -s sgp -f F1 -D -R\n",
-       "    Conventions:           ARM-1.3\n",
-       "    process_version:       ingest-aaf2dsme-1.2-0.el7\n",
-       "    dod_version:           aaf2dsv-c1-1.1\n",
-       "    input_source:          /data/project/ENG0004504/collection/sgp/sgpaaf2dsF...\n",
-       "    site_id:               sgp\n",
-       "    ...                    ...\n",
-       "    facility_id:           F1\n",
-       "    data_level:            c1\n",
-       "    location_description:  Southern Great Plains (SGP), Gulfstream 159 ("G1")...\n",
-       "    datastream:            sgpaaf2dsvF1.c1\n",
-       "    doi:                   10.5439/1419323\n",
-       "    history:               created by user burk on machine prod-proc5.adc.arm...
" - ], - "text/plain": [ - "\n", - "Dimensions: (time: 10097, optical_diameter: 61, bound: 2)\n", - "Coordinates:\n", - " * time (time) datetime64[ns] 2016-09-22T16:06:25 ......\n", - " * optical_diameter (optical_diameter) float32 10.0 20.0 ... inf\n", - "Dimensions without coordinates: bound\n", - "Data variables:\n", - " base_time datetime64[ns] 2016-09-22\n", - " time_offset (time) datetime64[ns] 2016-09-22T16:06:25 ......\n", - " optical_diameter_bounds (optical_diameter, bound) float32 5.0 ... inf\n", - " total_number_concentration (time) float32 26.59 0.0 22.4 ... 0.0 0.0 nan\n", - " number_concentration (time, optical_diameter) float32 2.24 ... nan\n", - " lat (time) float32 36.76 36.76 36.76 ... 36.74 36.74\n", - " lon (time) float32 -96.01 -96.01 ... -96.02 -96.02\n", - " alt (time) float32 220.0 224.0 228.0 ... 677.0 674.0\n", - "Attributes: (12/13)\n", - " command_line: aaf2dsme_ingest -s sgp -f F1 -D -R\n", - " Conventions: ARM-1.3\n", - " process_version: ingest-aaf2dsme-1.2-0.el7\n", - " dod_version: aaf2dsv-c1-1.1\n", - " input_source: /data/project/ENG0004504/collection/sgp/sgpaaf2dsF...\n", - " site_id: sgp\n", - " ... ...\n", - " facility_id: F1\n", - " data_level: c1\n", - " location_description: Southern Great Plains (SGP), Gulfstream 159 (\"G1\")...\n", - " datastream: sgpaaf2dsvF1.c1\n", - " doi: 10.5439/1419323\n", - " history: created by user burk on machine prod-proc5.adc.arm..." - ] - }, - "execution_count": 10, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "ds_single_1 = xr.load_dataset(\"/data/archive/sgp/sgpaaf2dsvF1.c1/sgpaaf2dsvF1.c1.20160921.163940.nc\")\n", - "ds_single_1\n", - "\n", - "ds_single_2 = xr.load_dataset(\"/data/archive/sgp/sgpaaf2dsvF1.c1/sgpaaf2dsvF1.c1.20160922.160625.nc\")\n", - "ds_single_2" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "id": "b0e5d0d7", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "2 files loaded\n" - ] - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "
<xarray.Dataset>\n",
-       "Dimensions:                     (time: 18380, optical_diameter: 61, bound: 2)\n",
-       "Coordinates:\n",
-       "  * time                        (time) datetime64[ns] 2016-09-21T16:39:40 ......\n",
-       "  * optical_diameter            (optical_diameter) float32 10.0 20.0 ... inf\n",
-       "Dimensions without coordinates: bound\n",
-       "Data variables:\n",
-       "    base_time                   (time) datetime64[ns] 2016-09-21 ... 2016-09-22\n",
-       "    time_offset                 (time) datetime64[ns] 2016-09-21T16:39:40 ......\n",
-       "    optical_diameter_bounds     (time, optical_diameter, bound) float32 dask.array<chunksize=(8283, 61, 2), meta=np.ndarray>\n",
-       "    total_number_concentration  (time) float32 dask.array<chunksize=(8283,), meta=np.ndarray>\n",
-       "    number_concentration        (time, optical_diameter) float32 dask.array<chunksize=(8283, 61), meta=np.ndarray>\n",
-       "    lat                         (time) float32 dask.array<chunksize=(8283,), meta=np.ndarray>\n",
-       "    lon                         (time) float32 dask.array<chunksize=(8283,), meta=np.ndarray>\n",
-       "    alt                         (time) float32 dask.array<chunksize=(8283,), meta=np.ndarray>\n",
-       "Attributes: (12/17)\n",
-       "    command_line:          aaf2dsme_ingest -s sgp -f F1 -D -R\n",
-       "    Conventions:           ARM-1.3\n",
-       "    process_version:       ingest-aaf2dsme-1.2-0.el7\n",
-       "    dod_version:           aaf2dsv-c1-1.1\n",
-       "    input_source:          /data/project/ENG0004504/collection/sgp/sgpaaf2dsF...\n",
-       "    site_id:               sgp\n",
-       "    ...                    ...\n",
-       "    doi:                   10.5439/1419323\n",
-       "    history:               created by user burk on machine prod-proc5.adc.arm...\n",
-       "    _file_dates:           ['20160921', '20160922']\n",
-       "    _file_times:           ['163940', '160625']\n",
-       "    _datastream:           sgpaaf2dsvF1.c1\n",
-       "    _arm_standards_flag:   1
" - ], - "text/plain": [ - "\n", - "Dimensions: (time: 18380, optical_diameter: 61, bound: 2)\n", - "Coordinates:\n", - " * time (time) datetime64[ns] 2016-09-21T16:39:40 ......\n", - " * optical_diameter (optical_diameter) float32 10.0 20.0 ... inf\n", - "Dimensions without coordinates: bound\n", - "Data variables:\n", - " base_time (time) datetime64[ns] 2016-09-21 ... 2016-09-22\n", - " time_offset (time) datetime64[ns] 2016-09-21T16:39:40 ......\n", - " optical_diameter_bounds (time, optical_diameter, bound) float32 dask.array\n", - " total_number_concentration (time) float32 dask.array\n", - " number_concentration (time, optical_diameter) float32 dask.array\n", - " lat (time) float32 dask.array\n", - " lon (time) float32 dask.array\n", - " alt (time) float32 dask.array\n", - "Attributes: (12/17)\n", - " command_line: aaf2dsme_ingest -s sgp -f F1 -D -R\n", - " Conventions: ARM-1.3\n", - " process_version: ingest-aaf2dsme-1.2-0.el7\n", - " dod_version: aaf2dsv-c1-1.1\n", - " input_source: /data/project/ENG0004504/collection/sgp/sgpaaf2dsF...\n", - " site_id: sgp\n", - " ... ...\n", - " doi: 10.5439/1419323\n", - " history: created by user burk on machine prod-proc5.adc.arm...\n", - " _file_dates: ['20160921', '20160922']\n", - " _file_times: ['163940', '160625']\n", - " _datastream: sgpaaf2dsvF1.c1\n", - " _arm_standards_flag: 1" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Load files as a single dataset\n", - "files_list = files_filter \n", - "ds = act.io.armfiles.read_netcdf(files_list)\n", - "ds.clean.cleanup()\n", - "print(f'{len(files_list)} files loaded')\n", - "ds\n" - ] - }, - { - "cell_type": "markdown", - "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", - "metadata": {}, - "source": [ - "## Plot time series data\n", - "#### Define the list of variables to be plotted:" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "id": "d09b789e-84f1-4605-846b-a72c110c8048", - "metadata": {}, - "outputs": [], - "source": [ - "variables_to_plot = ['total_number_concentration', 'number_concentration']" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", - "metadata": {}, - "outputs": [ - { - "ename": "ValueError", - "evalue": "x and y arguments to pcolormesh cannot have non-finite values or be of type numpy.ma.core.MaskedArray with masked values", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", - "Cell \u001b[0;32mIn[9], line 5\u001b[0m\n\u001b[1;32m 2\u001b[0m ts_display\u001b[38;5;241m.\u001b[39madd_subplots((\u001b[38;5;28mlen\u001b[39m(variables_to_plot),), figsize \u001b[38;5;241m=\u001b[39m (\u001b[38;5;241m9.5\u001b[39m,\u001b[38;5;241m4\u001b[39m\u001b[38;5;241m*\u001b[39m\u001b[38;5;28mlen\u001b[39m(variables_to_plot)))\n\u001b[1;32m 4\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m i,v \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28menumerate\u001b[39m(variables_to_plot):\n\u001b[0;32m----> 5\u001b[0m ts_ax \u001b[38;5;241m=\u001b[39m \u001b[43mts_display\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mplot\u001b[49m\u001b[43m(\u001b[49m\u001b[43mv\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43msubplot_index\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mi\u001b[49m\u001b[43m,\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mset_title\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mds\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mvariables\u001b[49m\u001b[43m[\u001b[49m\u001b[43mv\u001b[49m\u001b[43m]\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mattrs\u001b[49m\u001b[43m[\u001b[49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43mlong_name\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m,\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 6\u001b[0m ts_ax\u001b[38;5;241m.\u001b[39mgrid()\n\u001b[1;32m 8\u001b[0m plt\u001b[38;5;241m.\u001b[39mshow()\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/act/plotting/timeseriesdisplay.py:588\u001b[0m, in \u001b[0;36mTimeSeriesDisplay.plot\u001b[0;34m(self, field, dsname, subplot_index, cmap, set_title, add_nan, day_night_background, invert_y_axis, abs_limits, time_rng, y_rng, use_var_for_y, set_shading, assessment_overplot, overplot_marker, overplot_behind, overplot_markersize, assessment_overplot_category, assessment_overplot_category_color, force_line_plot, labels, cbar_label, cbar_h_adjust, secondary_y, y_axis_flag_meanings, colorbar_labels, cb_friendly, **kwargs)\u001b[0m\n\u001b[1;32m 586\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124medgecolors\u001b[39m\u001b[38;5;124m'\u001b[39m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;129;01min\u001b[39;00m kwargs\u001b[38;5;241m.\u001b[39mkeys():\n\u001b[1;32m 587\u001b[0m kwargs[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124medgecolors\u001b[39m\u001b[38;5;124m'\u001b[39m] \u001b[38;5;241m=\u001b[39m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mface\u001b[39m\u001b[38;5;124m'\u001b[39m\n\u001b[0;32m--> 588\u001b[0m mesh \u001b[38;5;241m=\u001b[39m \u001b[43max\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mpcolormesh\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 589\u001b[0m \u001b[43m \u001b[49m\u001b[43mnp\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43masarray\u001b[49m\u001b[43m(\u001b[49m\u001b[43mxdata\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 590\u001b[0m \u001b[43m \u001b[49m\u001b[43mydata\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 591\u001b[0m \u001b[43m \u001b[49m\u001b[43mdata\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mtranspose\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 592\u001b[0m \u001b[43m \u001b[49m\u001b[43mshading\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mset_shading\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 593\u001b[0m \u001b[43m \u001b[49m\u001b[43mcmap\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcmap\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 594\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 595\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 597\u001b[0m \u001b[38;5;66;03m# Set Title\u001b[39;00m\n\u001b[1;32m 598\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m set_title \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/matplotlib/__init__.py:1442\u001b[0m, in \u001b[0;36m_preprocess_data..inner\u001b[0;34m(ax, data, *args, **kwargs)\u001b[0m\n\u001b[1;32m 1439\u001b[0m \u001b[38;5;129m@functools\u001b[39m\u001b[38;5;241m.\u001b[39mwraps(func)\n\u001b[1;32m 1440\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21minner\u001b[39m(ax, \u001b[38;5;241m*\u001b[39margs, data\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mNone\u001b[39;00m, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs):\n\u001b[1;32m 1441\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m data \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[0;32m-> 1442\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mfunc\u001b[49m\u001b[43m(\u001b[49m\u001b[43max\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;28;43mmap\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43msanitize_sequence\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43margs\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 1444\u001b[0m bound \u001b[38;5;241m=\u001b[39m new_sig\u001b[38;5;241m.\u001b[39mbind(ax, \u001b[38;5;241m*\u001b[39margs, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs)\n\u001b[1;32m 1445\u001b[0m auto_label \u001b[38;5;241m=\u001b[39m (bound\u001b[38;5;241m.\u001b[39marguments\u001b[38;5;241m.\u001b[39mget(label_namer)\n\u001b[1;32m 1446\u001b[0m \u001b[38;5;129;01mor\u001b[39;00m bound\u001b[38;5;241m.\u001b[39mkwargs\u001b[38;5;241m.\u001b[39mget(label_namer))\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/matplotlib/axes/_axes.py:6220\u001b[0m, in \u001b[0;36mAxes.pcolormesh\u001b[0;34m(self, alpha, norm, cmap, vmin, vmax, shading, antialiased, *args, **kwargs)\u001b[0m\n\u001b[1;32m 6217\u001b[0m shading \u001b[38;5;241m=\u001b[39m shading\u001b[38;5;241m.\u001b[39mlower()\n\u001b[1;32m 6218\u001b[0m kwargs\u001b[38;5;241m.\u001b[39msetdefault(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124medgecolors\u001b[39m\u001b[38;5;124m'\u001b[39m, \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mnone\u001b[39m\u001b[38;5;124m'\u001b[39m)\n\u001b[0;32m-> 6220\u001b[0m X, Y, C, shading \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_pcolorargs\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43mpcolormesh\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 6221\u001b[0m \u001b[43m \u001b[49m\u001b[43mshading\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mshading\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mkwargs\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 6222\u001b[0m coords \u001b[38;5;241m=\u001b[39m np\u001b[38;5;241m.\u001b[39mstack([X, Y], axis\u001b[38;5;241m=\u001b[39m\u001b[38;5;241m-\u001b[39m\u001b[38;5;241m1\u001b[39m)\n\u001b[1;32m 6223\u001b[0m \u001b[38;5;66;03m# convert to one dimensional array, except for 3D RGB(A) arrays\u001b[39;00m\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/matplotlib/axes/_axes.py:5717\u001b[0m, in \u001b[0;36mAxes._pcolorargs\u001b[0;34m(self, funcname, shading, *args, **kwargs)\u001b[0m\n\u001b[1;32m 5715\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m funcname \u001b[38;5;241m==\u001b[39m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mpcolormesh\u001b[39m\u001b[38;5;124m'\u001b[39m:\n\u001b[1;32m 5716\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m np\u001b[38;5;241m.\u001b[39mma\u001b[38;5;241m.\u001b[39mis_masked(X) \u001b[38;5;129;01mor\u001b[39;00m np\u001b[38;5;241m.\u001b[39mma\u001b[38;5;241m.\u001b[39mis_masked(Y):\n\u001b[0;32m-> 5717\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\n\u001b[1;32m 5718\u001b[0m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mx and y arguments to pcolormesh cannot have \u001b[39m\u001b[38;5;124m'\u001b[39m\n\u001b[1;32m 5719\u001b[0m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mnon-finite values or be of type \u001b[39m\u001b[38;5;124m'\u001b[39m\n\u001b[1;32m 5720\u001b[0m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mnumpy.ma.core.MaskedArray with masked values\u001b[39m\u001b[38;5;124m'\u001b[39m)\n\u001b[1;32m 5721\u001b[0m \u001b[38;5;66;03m# safe_masked_invalid() returns an ndarray for dtypes other\u001b[39;00m\n\u001b[1;32m 5722\u001b[0m \u001b[38;5;66;03m# than floating point.\u001b[39;00m\n\u001b[1;32m 5723\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(X, np\u001b[38;5;241m.\u001b[39mma\u001b[38;5;241m.\u001b[39mcore\u001b[38;5;241m.\u001b[39mMaskedArray):\n", - "\u001b[0;31mValueError\u001b[0m: x and y arguments to pcolormesh cannot have non-finite values or be of type numpy.ma.core.MaskedArray with masked values" - ] - }, - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "b4ce530bc3b34d038b85608090b4b719", - "version_major": 2, - "version_minor": 0 - }, - "image/png": "iVBORw0KGgoAAAANSUhEUgAAA7YAAAMgCAYAAAAXxf7GAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAA9hAAAPYQGoP6dpAAB6jElEQVR4nOzdeXhU5d3/8c+QZbKYjISYTQJERQQDLkEhoAIKQVaVPj9QbACLKA8qIlAFaUughSgq0kJBpIgLWHxaxFqgNLEiQsMmJpVNtIoEJCECIWFNQnL//lBGhyQQYLYT3q/rytWe+3zPmft8Z4jzyTlzxmaMMQIAAAAAwKIa+HoCAAAAAABcDIItAAAAAMDSCLYAAAAAAEsj2AIAAAAALI1gCwAAAACwNIItAAAAAMDSCLYAAAAAAEsj2AIAAAAALI1gCwAAAACwNIItAAAAAMDSCLYAAAAAAEsj2AIAAAAALI1gCwAAAACwNIItAAAAAMDSCLYAAAAAAEsj2AIAAAAALI1gCwAAAACwNIItAAAAAMDSCLYAAAAAAEsj2AIAAAAALI1gCwAAAACwNIItAAAAAMDSCLYAAAAAAEsj2AIAAAAALI1gCwAAAACwNIItAAAAAMDSCLYAAAAAAEsj2AIAAAAALI1gCwAAAACwNIItAAAAAMDSCLYAAAAAAEsj2AIAAAAALI1gCwAAAACwNIItAAAAAMDSCLYAAAAAAEsj2AIAAAAALI1gCwAAAACwNIItAAAAAMDSCLYAAAAAAEsj2AIAAAAALI1gCwAAAACwNIItAAAAAMDSCLYAAAAAAEsj2AIAAAAALI1gCwAAAACwNIItAAAAAMDSCLYAAAAAAEsj2AIAAAAALI1gCwAAAACwNIItAAAAAMDSCLYAAAAAAEsj2AIAAAAALI1gCwAAAACwNIItAAAAAMDSCLYAAAAAAEsj2AIAAAAALI1gCwAAAACwNIItAAAAAMDSCLYAAAAAAEsj2AIAAAAALI1gCwAAAACwNIItAAAAAMDSCLYAAAAAAEsj2AIAAAAALI1gCwAAAACwNIItAAAAAMDSCLYAAAAAAEsj2AIAAAAALI1gCwAAAACwNIItAAAAAMDSCLYAAAAAAEsj2AIAAAAALI1gCwAAAACwNIItAAAAAMDSCLYAAAAAAEsj2AJAPWGz2er089FHH51zX1OnTtV777130fPJyMi4qH34SrNmzdS7d29fT+OSsW/fPmVkZCgvL88j+8/JyVFGRoYOHz5cbV3nzp3VuXNnjzwuAMB7An09AQCAe6xbt85l+be//a1WrVqlDz/80GW8VatW59zX1KlT9T//8z+699573TlFoEb79u3TpEmT1KxZM914441u339OTo4mTZqkIUOG6PLLL3dZN3v2bLc/HgDA+wi2AFBPtG/f3mX5iiuuUIMGDaqNwz8YY3Ty5EmFhob6eiqWc/z4cYWFhbllX3X5Qw8AwP9xKTIAXEIOHTqkESNG6Morr1RwcLCuuuoqTZgwQWVlZc4am82mY8eO6Y033nBevnz6Us3vvvtOI0aMUKtWrXTZZZcpJiZGd955p9asWXNB8/nmm29ks9n04osvavr06UpKStJll12m1NRUrV+/3qW2tktGhwwZombNmlXb5wsvvKDnn39ezZo1U2hoqDp37qwvvvhCFRUVGjdunBISEuRwOHTfffepqKioxvktXbpUbdq0UUhIiK666ir94Q9/qFZTWlqqsWPHKikpScHBwbryyis1atQoHTt2zKXOZrPp8ccf1yuvvKKWLVvKbrfrjTfeOGt/3n77baWmpuqyyy7TZZddphtvvFHz5893qXnttdd0ww03KCQkRFFRUbrvvvu0Y8eOaj267LLL9N///lc9e/bUZZddpsTERI0ZM8bluZeksrIyTZ48WS1btlRISIgaNWqkLl26KCcnx1ljjNHs2bN14403KjQ0VA0bNtT//M//6Ouvv3bZV+fOnZWcnKxNmzbp9ttvV1hYmK666io999xzqqqqkiR99NFHuuWWWyRJDz30kPM1d/oy9tNz37Jli9LS0hQREaG77rpLkpSdna177rlHjRs3VkhIiK655ho9+uijOnDggHMOGRkZ+uUvfylJSkpKqnZJfk2vq7r8O/npc/rWW2+pZcuWCgsL0w033KBly5ad9XkFALgfZ2wB4BJx8uRJdenSRV999ZUmTZqkNm3aaM2aNcrMzFReXp6WL18u6ftLmu+880516dJFv/71ryVJkZGRkr5/wy9JEydOVFxcnI4ePaqlS5eqc+fO+te//nXBn1X84x//qOuuu04zZsyQJP36179Wz549tWvXLjkcjgveZ5s2bfTHP/5Rhw8f1pgxY9SnTx+1a9dOQUFBeu2117R7926NHTtWDz/8sN5//32X7fPy8jRq1ChlZGQoLi5OixYt0pNPPqny8nKNHTtW0vdnDjt16qS9e/fq2WefVZs2bbRt2zb95je/0ZYtW/TBBx/IZrM59/nee+9pzZo1+s1vfqO4uDjFxMTUOv/f/OY3+u1vf6t+/fppzJgxcjgc2rp1q3bv3u2syczM1LPPPqsHHnhAmZmZOnjwoDIyMpSamqpNmzapefPmztqKigr17dtXQ4cO1ZgxY/Txxx/rt7/9rRwOh37zm99Ikk6dOqUePXpozZo1GjVqlO68806dOnVK69evV35+vjp06CBJevTRR/X6669r5MiRev7553Xo0CFNnjxZHTp00H/+8x/FxsY6H7ewsFAPPvigxowZo4kTJ2rp0qUaP368EhISNGjQIN18881asGCBHnroIf3qV79Sr169JEmNGzd27qO8vFx9+/bVo48+qnHjxunUqVOSpK+++kqpqal6+OGH5XA49M0332j69Om67bbbtGXLFgUFBenhhx/WoUOHNHPmTL377ruKj4+XVPuZ2rr+Ozlt+fLl2rRpkyZPnqzLLrtM06ZN03333aedO3fqqquuqvX5BQC4mQEA1EuDBw824eHhzuVXXnnFSDL/93//51L3/PPPG0kmKyvLORYeHm4GDx58zsc4deqUqaioMHfddZe57777XNZJMhMnTjzr9rt27TKSTOvWrc2pU6ec4xs3bjSSzJ///GfnWKdOnUynTp1qPM6mTZtW2+cNN9xgKisrneMzZswwkkzfvn1dth81apSRZEpKSpxjTZs2NTabzeTl5bnUduvWzURGRppjx44ZY4zJzMw0DRo0MJs2bXKp++tf/2okmRUrVrj0w+FwmEOHDp21J8YY8/XXX5uAgADz4IMP1lpTXFxsQkNDTc+ePV3G8/Pzjd1uNwMHDnSODR48uMbnvmfPnqZFixbO5TfffNNIMvPmzav1cdetW2ckmZdeesllfM+ePSY0NNQ8/fTTzrFOnToZSWbDhg0uta1atTLdu3d3Lm/atMlIMgsWLKj2eKfn/tprr9U6J2OMqaqqMhUVFWb37t1Gkvnb3/7mXPfCCy8YSWbXrl3VtjvzdXU+/04kmdjYWFNaWuocKywsNA0aNDCZmZlnnS8AwL24FBkALhEffvihwsPD9T//8z8u40OGDJEk/etf/6rTfl555RXdfPPNCgkJUWBgoIKCgvSvf/2r2uWv56NXr14KCAhwLrdp00aSXM5Onq+ePXuqQYMf/zPXsmVL52P91Onx/Px8l/Hrr79eN9xwg8vYwIEDVVpaqk8//VSStGzZMiUnJ+vGG2/UqVOnnD/du3ev8Q7Ud955pxo2bHjOuWdnZ6uyslKPPfZYrTXr1q3TiRMnnM/faYmJibrzzjurPZ82m019+vRxGWvTpo1Lj//xj38oJCREv/jFL2p93GXLlslms+nnP/+5yzHHxcXphhtuqHbMcXFxuvXWW8/6uHXxs5/9rNpYUVGRhg8frsTEROdrsWnTppJ0wa/H8/130qVLF0VERDiXY2NjFRMTc1GvXQDA+eNSZAC4RBw8eFBxcXEul8ZKUkxMjAIDA3Xw4MFz7mP69OkaM2aMhg8frt/+9reKjo5WQECAfv3rX19UsG3UqJHLst1ulySdOHHigvcZFRXlshwcHHzW8ZMnT7qMx8XFVdvn6bHTvdq/f7/++9//KigoqMY5/PSznpKcl8Gey3fffSfJ9XLcM52eQ037TEhIUHZ2tstYWFiYQkJCXMbsdrvLcX/33XdKSEhw+YPAmfbv3y9jjMvlxj915uW3Zz63px/3fJ7bsLAw5+Xwp1VVVSktLU379u3Tr3/9a7Vu3Vrh4eGqqqpS+/btL/i1c77/TtxxfACAi0ewBYBLRKNGjbRhwwYZY1zetBcVFenUqVOKjo4+5z4WLlyozp07a86cOS7jR44ccft8zxQSEqKSkpJq42eGR3cpLCysdex0mImOjlZoaKhee+21GvdxZk/PDEu1ueKKKyRJe/fuVWJiYo01p+dQUFBQbd2+ffvq9HzW9Lhr165VVVVVreE2OjpaNptNa9ascf4B4qdqGrtYNfVt69at+s9//qPXX39dgwcPdo7/97//vajHcse/EwCA93EpMgBcIu666y4dPXpU7733nsv4m2++6Vx/Wm1nnGw2W7Xg8tlnn1X7Dl1PaNasmb744guXO9MePHjQ5W697rRt2zb95z//cRl7++23FRERoZtvvlmS1Lt3b3311Vdq1KiR2rZtW+3np3drPh9paWkKCAio9geEn0pNTVVoaKgWLlzoMr537159+OGHLs9nXfXo0UMnT57U66+/XmtN7969ZYzRt99+W+Mxt27d+rwf90LO0J8OnWe+HufOnXtR+z+ffycAAP/BGVsAuEQMGjRIf/zjHzV48GB98803at26tdauXaupU6eqZ8+e6tq1q7O2devW+uijj/T3v/9d8fHxioiIUIsWLdS7d2/99re/1cSJE9WpUyft3LlTkydPVlJSkvNOtZ6Snp6uuXPn6uc//7mGDRumgwcPatq0adUuUXWXhIQE9e3bVxkZGYqPj9fChQuVnZ2t559/3vkdqqNGjdKSJUt0xx136KmnnlKbNm1UVVWl/Px8ZWVlacyYMWrXrt15P3azZs307LPP6re//a1OnDihBx54QA6HQ9u3b9eBAwc0adIkXX755fr1r3+tZ599VoMGDdIDDzyggwcPatKkSQoJCdHEiRPP+3EfeOABLViwQMOHD9fOnTvVpUsXVVVVacOGDWrZsqXuv/9+dezYUY888ogeeughffLJJ7rjjjsUHh6ugoICrV27Vq1bt9b//u//ntfjXn311QoNDdWiRYvUsmVLXXbZZUpISFBCQkKt21x33XW6+uqrNW7cOBljFBUVpb///e/VLsGW5Azbv//97zV48GAFBQWpRYsWLp+NPe18/p0AAPwHwRYALhEhISFatWqVJkyYoBdeeEHfffedrrzySo0dO7ZaCPr973+vxx57TPfff7/zK20++ugjTZgwQcePH9f8+fM1bdo0tWrVSq+88oqWLl1a7aZB7taxY0e98cYbeu6553TPPffoqquu0sSJE7VixQqPPPaNN96ohx56SBMnTtSXX36phIQETZ8+XU899ZSzJjw8XGvWrNFzzz2nV199Vbt27VJoaKiaNGmirl27XvAZW0maPHmymjdvrpkzZ+rBBx9UYGCgmjdvrpEjRzprxo8fr5iYGP3hD3/QO++84/y+3qlTp7p81U9dBQYGasWKFcrMzNSf//xnzZgxQxEREbrhhht09913O+vmzp2r9u3ba+7cuZo9e7aqqqqUkJCgjh07VrtRVF2EhYXptdde06RJk5SWlqaKigpNnDjR+V22NQkKCtLf//53Pfnkk3r00UcVGBiorl276oMPPlCTJk1cajt37qzx48frjTfe0Lx581RVVaVVq1bV+PVU5/PvBADgP2zGGOPrSQAAAAAAcKH4jC0AAAAAwNIItgAAAAAASyPYAgAAAAAsjWALAAAAALA0gi0AAAAAwNIItgAAAAAAS+N7bOupqqoq7du3TxEREbLZbL6eDgAAAFCvGWN05MgRJSQkqEED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SVlZWdq+fbsWLlyom266SV27dtVLL72kefPmqbS01PtNAQAAAAA/R7D9iccee0y9evVS165dXcZ37dqlwsJCpaWlOcfsdrs6deqknJwcSdLmzZtVUVHhUpOQkKDk5GRnzbp16+RwONSuXTtnTfv27eVwOFxqkpOTlZCQ4Kzp3r27ysrKtHnzZvcfNAAAAABYXKCvJ+AuFRUVKiws1PHjx3XFFVcoKirqvLZfvHixPv30U23atKnausLCQklSbGysy3hsbKx2797trAkODnY503u65vT2hYWFiomJqbb/mJgYl5ozH6dhw4YKDg521tSkrKxMZWVlzuXTZ3crKipUUVFR63bedHoe5zOfU2fWGuM3x+NrF9JPnB09dS/66V70073op3vRT/ein+7lrX7yfPmWpYPt0aNHtWjRIv35z3/Wxo0bXYJd48aNlZaWpkceeUS33HLLWfezZ88ePfnkk8rKylJISEitdTabzWXZGFNt7Exn1tRUfyE1Z8rMzNSkSZOqjWdlZSksLOysc/S27OzsOtcerZB++jItKyvTihUr3D8pCzuffqJu6Kl70U/3op/uRT/di366F/10L0/38/jx4x7dP87OssH25Zdf1pQpU9SsWTP17dtX48aN05VXXqnQ0FAdOnRIW7du1Zo1a9StWze1b99eM2fOVPPmzWvc1+bNm1VUVKSUlBTnWGVlpT7++GPNmjXL+fnXwsJCxcfHO2uKioqcZ1fj4uJUXl6u4uJil7O2RUVF6tChg7Nm//791R7/u+++c9nPhg0bXNYXFxeroqKi2pncnxo/frxGjx7tXC4tLVViYqLS0tIUGRlZ63beVFFRoezsbHXr1k1BQUF12ubgsXJN+OQj57LdblfPnp09M0GLuZB+4uzoqXvRT/ein+5FP92LfroX/XQvb/WT++H4lmWDbU5OjlatWqXWrVvXuP7WW2/VL37xC82ZM0evvfaaVq9eXWuwveuuu7RlyxaXsYceekjXXXednnnmGV111VWKi4tTdna2brrpJklSeXm5Vq9ereeff16SlJKSoqCgIGVnZ6t///6SpIKCAm3dulXTpk2TJKWmpqqkpEQbN27UrbfeKknasGGDSkpKnOE3NTVVU6ZMUUFBgTNEZ2VlyW63uwTvM9ntdtnt9mrjQUFBfvcL8XzmFBRY5Tpgs/nd8fiaPz7HVkdP3Yt+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s9n03nvvuaw/evSoHn/8cTVu3FihoaFq2bKl5syZc879btmyRZ06dVJoaKiuvPJKTZ48WWfez2z16tVKSUlRSEiIrrrqKr3yyivuPDSvy8zM1C233KKIiAjFxMTo3nvv1c6dO11q3n33XXXv3l3R0dGy2WzKy8ur074vxX6eNnv2bCUlJSkkJEQpKSlas2aNc50xRhkZGUpISFBoaKg6d+6sbdu2nXOfl3I/pbP3NCMjQ9ddd53Cw8PVsGFDde3aVRs2bDjnPi/lnp6tn5K0Y8cO9e3bVw6HQxEREWrfvr3y8/PPuk/6WXM/9+/fryFDhighIUFhYWG6++679eWXX55zn5dqP8/23/iKigo988wzat26tcLDw5WQkKBBgwZp375959wv/az5PdOQIUNks9lcftq3b3/O/dJP3oPiHAxQBytWrDATJkwwS5YsMZLM0qVLXdY//PDD5uqrrzarVq0yu3btMnPnzjUBAQHmvffeq3WfJSUlJjY21tx///1my5YtZsmSJSYiIsK8+OKLzpqvv/7ahIWFmSeffNJs377dzJs3zwQFBZm//vWvnjpUj+vevbtZsGCB2bp1q8nLyzO9evUyTZo0MUePHnXWvPnmm2bSpElm3rx5RpLJzc09534v1X4aY8zixYtNUFCQmTdvntm+fbt58sknTXh4uNm9e7cxxpjnnnvOREREmCVLlpgtW7aYAQMGmPj4eFNaWlrrPi/lfhpz7p4uWrTIZGdnm6+++sps3brVDB061ERGRpqioqJa93kp9/Rc/fzvf/9roqKizC9/+Uvz6aefmq+++sosW7bM7N+/v9Z90s+a+1lVVWXat29vbr/9drNx40bz+eefm0ceeaTa79kzXcr9PNt/4w8fPmy6du1q3nnnHfP555+bdevWmXbt2pmUlJSz7pN+1v6eafDgwebuu+82BQUFzp+DBw+edZ/0k/egODeCLc5bTb9Urr/+ejN58mSXsZtvvtn86le/qnU/s2fPNg6Hw5w8edI5lpmZaRISEkxVVZUxxpinn37aXHfddS7bPfroo6Z9+/YXeRT+o6ioyEgyq1evrrZu165ddQ62l3I/b731VjN8+HCXseuuu86MGzfOVFVVmbi4OPPcc8851508edI4HA7zyiuv1LrPS7mfxpy9pzUpKSkxkswHH3xQ6z4v5Z6eq58DBgwwP//5z89rn/Sz5n7u3LnTSDJbt251rjt16pSJiooy8+bNq3Wfl3I/f6qm/8afaePGjUaS8w8zNaGf36st2N5zzz3ntR/6+T3eg+JsuBQZbnHbbbfp/fff17fffitjjFatWqUvvvhC3bt3d9YMGTJEnTt3di6vW7dOnTp1cvmy7O7du2vfvn365ptvnDVpaWkuj9W9e3d98sknqqio8OgxeUtJSYkkKSoq6ry2o5/fKy8v1+bNm6sdV1pamnJycrRr1y4VFha6rLfb7erUqZNycnKcY/TzR+fqaU31r776qhwOh2644QbnOD393rn6WVVVpeXLl+vaa69V9+7dFRMTo3bt2tV4+SL9PHc/y8rKJEkhISHOdQEBAQoODtbatWudY/TzwpWUlMhms+nyyy93jtHP8/PRRx8pJiZG1157rYYNG6aioiKX9fSz7ngPitMItnCLP/zhD2rVqpUaN26s4OBg3X333Zo9e7Zuu+02Z018fLyaNGniXC4sLFRsbKzLfk4vFxYWnrXm1KlTOnDggKcOx2uMMRo9erRuu+02JScnn9e29PN7Bw4cUGVlZY3HVVhY6Dz22tafRj9/dK6enrZs2TJddtllCgkJ0csvv6zs7GxFR0c719PT752rn0VFRTp69Kiee+453X333crKytJ9992nfv36afXq1c56+vm9c/XzuuuuU9OmTTV+/HgVFxervLxczz33nAoLC1VQUOCsp58X5uTJkxo3bpwGDhyoyMhI5zj9rLsePXpo0aJF+vDDD/XSSy9p06ZNuvPOO51/lJHo5/ngPShOC/T1BFA//OEPf9D69ev1/vvvq2nTpvr44481YsQIxcfHq2vXrpK+v2nSmWw2m8uy+eFD+z8dr0uNVT3++OP67LPPXM4i1BX9dFXTcZ3ruH86Rj+rO1fPunTpory8PB04cEDz5s1T//79tWHDBsXExEiip2eqrZ9VVVWSpHvuuUdPPfWUJOnGG29UTk6OXnnlFXXq1EkS/TxTbf0MCgrSkiVLNHToUEVFRSkgIEBdu3ZVjx49XOrp5/mrqKjQ/fffr6qqKs2ePdtlHf2suwEDBjj/f3Jystq2baumTZtq+fLl6tevnyT6eT54D4rTCLa4aCdOnNCzzz6rpUuXqlevXpKkNm3aKC8vTy+++KLzl8qZ4uLiXM7+SHJeinP6L2S11QQGBqpRo0buPhSveuKJJ/T+++/r448/VuPGjS96f5dqP6OjoxUQEFDjccXGxiouLk7S9395jY+Pr7a+NpdqP6Vz9/S08PBwXXPNNbrmmmvUvn17NW/eXPPnz9f48eNr3O+l2tNz9TM6OlqBgYFq1aqVy/qWLVue9Y9e9LP212dKSory8vJUUlKi8vJyXXHFFWrXrp3atm1b634v1X7WVUVFhfr3769du3bpww8/dDlbWxP6WXfx8fFq2rTpWe/cTT9rxntQ/BSXIuOiVVRUqKKiQg0auL6cAgICnGciapKamqqPP/5Y5eXlzrGsrCwlJCSoWbNmzprs7GyX7bKystS2bVsFBQW57yC8yBijxx9/XO+++64+/PBDJSUluWW/l2o/g4ODlZKSUu24srOz1aFDByUlJSkuLs5lfXl5uVavXq0OHTrUut9LtZ/SuXtaG2OMy6V0Z7pUe3qufgYHB+uWW26p9rVfX3zxhZo2bVrrfunnuV+fDodDV1xxhb788kt98sknuueee2rd76Xaz7o4HWq//PJLffDBB3V6U08/6+7gwYPas2ePyx9fz0Q/a8Z7ULjwzj2qYHVHjhwxubm5Jjc310gy06dPN7m5uc47Inbq1Mlcf/31ZtWqVebrr782CxYsMCEhIWb27NnOfYwbN86kp6c7lw8fPmxiY2PNAw88YLZs2WLeffddExkZWeOt1p966imzfft2M3/+fMvfav1///d/jcPhMB999JHLrf6PHz/urDl48KDJzc01y5cvN5LM4sWLTW5urikoKHDW0M8fnf7qj/nz55vt27ebUaNGmfDwcPPNN98YY77/uh+Hw2Heffdds2XLFvPAAw9U+7of+unqbD09evSoGT9+vFm3bp355ptvzObNm83QoUON3W53uRMtPf3RuV6j7777rgkKCjKvvvqq+fLLL83MmTNNQECAWbNmjXMf9PNH5+rn//3f/5lVq1aZr776yrz33numadOmpl+/fi77oJ8/Ott/4ysqKkzfvn1N48aNTV5enst/t8rKypz7oJ8/Ols/jxw5YsaMGWNycnLMrl27zKpVq0xqaqq58sor+W9SLXgPiroi2KJOVq1aZSRV+xk8eLAxxpiCggIzZMgQk5CQYEJCQkyLFi3MSy+95LxlujHf396+U6dOLvv97LPPzO23327sdruJi4szGRkZLtsYY8xHH31kbrrpJhMcHGyaNWtm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07nRo7qhT+dGj+qGPvkW32MLAAAAALA0LkUGAAAAAFgawRYAAAAAYGkEWwAAAACApRFsAQAAAACWRrD1U5mZmbrlllsUERGhmJgY3Xvvvdq5c6dLjTFGGRkZSkhIUGhoqDp37qxt27a51JSVlemJJ55QdHS0wsPD1bdvX+3du9elpri4WOnp6XI4HHI4HEpPT9fhw4c9fYhu4c0+TZkyRR06dFBYWJguv/xyTx+a23irR998842GDh2qpKQkhYaG6uqrr9bEiRNVXl7uleO8WN58LfXt21dNmjRRSEiI4uPjlZ6ern379nn8GC+WN3v009obb7xRNptNeXl5njo0t/Jmn5o1ayabzebyM27cOI8fozt4+/W0fPlytWvXTqGhoYqOjla/fv08enzu4K0effTRR9VeR6d/Nm3a5JVjvRjefC198cUXuueeexQdHa3IyEh17NhRq1at8vgxuoM3+/Tpp5+qW7duuvzyy9WoUSM98sgjOnr0qMeP8WK5q0evvvqqOnfurMjISNlsthrfV1v5/bffMvBL3bt3NwsWLDBbt241eXl5plevXqZJkybm6NGjzprnnnvOREREmCVLlpgtW7aYAQMGmPj4eFNaWuqsGT58uLnyyitNdna2+fTTT02XLl3MDTfcYE6dOuWsufvuu01ycrLJyckxOTk5Jjk52fTu3durx3uhvNmn3/zmN2b69Olm9OjRxuFwePMwL4q3evSPf/zDDBkyxPzzn/80X331lfnb3/5mYmJizJgxY7x+zBfCm6+l6dOnm3Xr1plvvvnG/Pvf/zapqakmNTXVq8d7IbzZo9NGjhxpevToYSSZ3NxcbxzmRfNmn5o2bWomT55sCgoKnD9Hjhzx6vFeKG/26a9//atp2LChmTNnjtm5c6f5/PPPzV/+8hevHu+F8FaPysrKXF5DBQUF5uGHHzbNmjUzVVVVXj/u8+XN19I111xjevbsaf7zn/+YL774wowYMcKEhYWZgoICrx7zhfBWn7799lvTsGFDM3z4cPP555+bjRs3mg4dOpif/exnXj/m8+WuHr388ssmMzPTZGZmGkmmuLi42mNZ+f23vyLYWkRRUZGRZFavXm2MMaaqqsrExcWZ5557zllz8uRJ43A4zCuvvGKMMebw4cMmKCjILF682Fnz7bffmgYNGpiVK1caY4zZvn27kWTWr1/vrFm3bp2RZD7//HNvHJpbeapPP7VgwQJLBdszeaNHp02bNs0kJSV56Eg8y5t9+tvf/mZsNpspLy/30NF4hqd7tGLFCnPdddeZbdu2WSrYnsmTfWratKl5+eWXvXMgHuapPlVUVJgrr7zS/OlPf/Li0XiGt34vlZeXm5iYGDN58mQPHo3neKpP3333nZFkPv74Y2dNaWmpkWQ++OADbxyaW3mqT3PnzjUxMTGmsrLSWZObm2skmS+//NIbh+Y2F9Kjn1q1alWNwba+vf/2F1yKbBElJSWSpKioKEnSrl27VFhYqLS0NGeN3W5Xp06dlJOTI0navHmzKioqXGoSEhKUnJzsrFm3bp0cDofatWvnrGnfvr0cDoezxko81af6xJs9KikpcT6O1XirT4cOHdKiRYvUoUMHBQUFeepwPMKTPdq/f7+GDRumt956S2FhYd44HI/x9Gvp+eefV6NGjXTjjTdqypQplrn8/0ye6tOnn36qb7/9Vg0aNNBNN92k+Ph49ejRo9qlg1bgrd9L77//vg4cOKAhQ4Z46Eg8y1N9atSokVq2bKk333xTx44d06lTpzR37lzFxsYqJSXFW4fnNp7qU1lZmYKDg9WgwY8xIzQ0VJK0du1azx6Um11Ij+qivr3/9hcEWwswxmj06NG67bbblJycLEkqLCyUJMXGxrrUxsbGOtcVFhYqODhYDRs2PGtNTExMtceMiYlx1liFJ/tUX3izR1999ZVmzpyp4cOHu/swPM4bfXrmmWcUHh6uRo0aKT8/X3/72988dTge4ckeGWM0ZMgQDR8+XG3btvX0oXiUp19LTz75pBYvXqxVq1bp8ccf14wZMzRixAhPHpJHeLJPX3/9tSQpIyNDv/rVr7Rs2TI1bNhQnTp10qFDhzx6XO7kzd/f8+fPV/fu3ZWYmOjuw/A4T/bJZrMpOztbubm5ioiIUEhIiF5++WWtXLnSUvfekDzbpzvvvFOFhYV64YUXVF5eruLiYj377LOSpIKCAo8elztdaI/qoj69//YnBFsLePzxx/XZZ5/pz3/+c7V1NpvNZdkYU23sTGfW1FRfl/34G0/3qT7wVo/27dunu+++W//v//0/Pfzwwxc3aR/wRp9++ctfKjc3V1lZWQoICNCgQYNkjLn4yXuJJ3s0c+ZMlZaWavz48e6bsI94+rX01FNPqVOnTmrTpo0efvhhvfLKK5o/f74OHjzongPwEk/2qaqqSpI0YcIE/exnP1NKSooWLFggm82mv/zlL246As/z1u/vvXv36p///KeGDh16cRP2EU/2yRijESNGKCYmRmvWrNHGjRt1zz33qHfv3pYKbJJn+3T99dfrjTfe0EsvvaSwsDDFxcXpqquuUmxsrAICAtx3EB7m7h6dax8Xuh/8iGDr55544gm9//77WrVqlRo3buwcj4uLk6Rqf9UpKipy/hUpLi7O+Zeys9Xs37+/2uN+99131f4a5c883af6wFs92rdvn7p06aLU1FS9+uqrnjgUj/JWn6Kjo3XttdeqW7duWrx4sVasWKH169d74pDcztM9+vDDD7V+/XrZ7XYFBgbqmmuukSS1bdtWgwcP9thxuZsvfi+1b99ekvTf//7XLcfgDZ7uU3x8vCSpVatWzvV2u11XXXWV8vPz3X9AHuDN19KCBQvUqFEj9e3b192H4XHe+N20bNkyLV68WB07dtTNN9+s2bNnKzQ0VG+88YYnD82tvPF6GjhwoAoLC/Xtt9/q4MGDysjI0HfffaekpCRPHZZbXUyP6qK+vP/2Ox79BC8uWFVVlXnsscdMQkKC+eKLL2pcHxcXZ55//nnnWFlZWY0f8H/nnXecNfv27avx5lEbNmxw1qxfv94yH173Vp9+ymo3j/Jmj/bu3WuaN29u7r///hrvcOvPfPFaOi0/P99IMqtWrXLfAXmAt3q0e/dus2XLFufPP//5TyPJ/PWvfzV79uzx8FFePF++lv7+978bSWb37t1uPCLP8FafSkpKjN1ud7l51OmbI82dO9dTh+cW3n4tVVVVmaSkJMvczf40b/Xp/fffNw0aNKh25/Frr73WTJkyxROH5la+/N00f/58ExYWVuPdgf2JO3r0U+e6eZRV33/7K4Ktn/rf//1f43A4zEcffeRy+/3jx487a5577jnjcDjMu+++a7Zs2WIeeOCBGm/J3rhxY/PBBx+YTz/91Nx55501ft1PmzZtzLp168y6detM69atLXO7cW/2affu3SY3N9dMmjTJXHbZZSY3N9fk5ub6/VdreKtH3377rbnmmmvMnXfeafbu3evyWFbgrT5t2LDBzJw50+Tm5ppvvvnGfPjhh+a2224zV199tTl58qTXj/t8ePPf20/t2rXLUndF9lafcnJyzPTp001ubq75+uuvzTvvvGMSEhJM3759vX7MF8Kbr6cnn3zSXHnlleaf//yn+fzzz83QoUNNTEyMOXTokFeP+Xx5+9/cBx98YCSZ7du3e+0Y3cFbffruu+9Mo0aNTL9+/UxeXp7ZuXOnGTt2rAkKCjJ5eXleP+7z5c3X08yZM83mzZvNzp07zaxZs0xoaKj5/e9/79XjvRDu6lFBQYHJzc018+bNc95JOzc31xw8eNBZY+X33/6KYOunJNX4s2DBAmdNVVWVmThxoomLizN2u93ccccdZsuWLS77OXHihHn88cdNVFSUCQ0NNb179zb5+fkuNQcPHjQPPvigiYiIMBEREebBBx/0+7+onebNPg0ePLjGx/L3s2ze6tGCBQtqfSwr8FafPvvsM9OlSxcTFRVl7Ha7adasmRk+fLjZu3evtw71gnnz39tPWS3YeqtPmzdvNu3atTMOh8OEhISYFi1amIkTJ5pjx45561AvijdfT+Xl5WbMmDEmJibGREREmK5du5qtW7d64zAvirf/zT3wwAOmQ4cOnj4st/NmnzZt2mTS0tJMVFSUiYiIMO3btzcrVqzwxmFeNG/2KT093URFRZng4GDTpk0b8+abb3rjEC+au3o0ceLEc+7Hyu+//ZXNGAvdrQQAAAAAgDNw8ygAAAAAgKURbAEAAAAAlkawBQAAAABYGsEWAAAAAGBpBFsAAAAAgKURbAEAAAAAlkawBQAAAABYGsEWAAAAAGBpBFsAAAAAgKURbAEAAAAAlkaw/f/t1wEJAAAAgKD/r9sR6AsBAABYE1sAAADWxBYAAIA1sQUAAGBNbAEAAFgTWwAAANbEFgAAgDWxBQAAYE1sAQAAWBNbAAAA1sQWAACANbEFAABgTWwBAABYE1sAAADWxBYAAIA1sQUAAGBNbAEAAFgTWwAAANbEFgAAgDWxBQAAYE1sAQAAWBNbAAAA1sQWAACANbEFAABgTWwBAABYE1sAAADWxBYAAIA1sQUAAGBNbAEAAFgTWwAAANbEFgAAgDWxBQAAYE1sAQAAWBNbAAAA1sQWAACANbEFAABgTWwBAABYE1sAAADWxBYAAIA1sQUAAGBNbAEAAFgTWwAAANbEFgAAgDWxBQAAYE1sAQAAWBNbAAAA1sQWAACANbEFAABgTWwBAABYE1sAAADWxBYAAIA1sQUAAGBNbAEAAFgTWwAAANbEFgAAgDWxBQAAYC1l8f/E0y7UQgAAAABJRU5ErkJggg==", - "text/html": [ - "\n", - "
\n", - "
\n", - " Figure\n", - "
\n", - " \n", - "
\n", - " " - ], - "text/plain": [ - "Canvas(toolbar=Toolbar(toolitems=[('Home', 'Reset original view', 'home', 'home'), ('Back', 'Back to previous …" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", - "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", - "\n", - "for i,v in enumerate(variables_to_plot):\n", - " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", - " ts_ax.grid()\n", - "\n", - "plt.show()\n" - ] - }, - { - "cell_type": "markdown", - "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", - "metadata": {}, - "source": [ - "## Field selection dropdown menu\n", - "Select variable to be plotted from a dropdown menu" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", - "metadata": {}, - "outputs": [], - "source": [ - "plt.ioff()\n", - "\n", - "# populate dropdown menu with available variables \n", - "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", - "d_variable = 'total_number_concentration'\n", - "dropdown = widgets.Dropdown(\n", - " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", - " value= d_variable,\n", - " description='Field:',\n", - " disabled=False,\n", - ")\n", - "dropdown.layout.margin = '0px 30% 0px 20%'\n", - "dropdown.layout.width = '50%'\n", - "\n", - "# set up display\n", - "i_display = act.plotting.TimeSeriesDisplay(ds)\n", - "i_display.add_subplots((1,), figsize = (9.5,5))\n", - "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", - "i_ax.grid()\n", - "i_fig = i_display.fig\n", - "\n", - "# update plot callback function\n", - "def update_plot(change):\n", - " i_ax.cla()\n", - " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", - " i_ax_new.grid()\n", - " i_fig.canvas.draw()\n", - " i_fig.canvas.flush_events()\n", - "\n", - "dropdown.observe(update_plot, names='value')\n", - "\n", - "widgets.AppLayout(\n", - " header=dropdown,\n", - " center=i_fig.canvas,\n", - " pane_heights=[1, 6,1]\n", - ")\n" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.16" - }, - "widgets": { - "application/vnd.jupyter.widget-state+json": { - "state": {}, - "version_major": 2, - "version_minor": 0 - } - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/VAPs/quicklook/AOP/.ipynb_checkpoints/aoppsap1flynn1m.c1-checkpoint.ipynb b/VAPs/quicklook/AOP/.ipynb_checkpoints/aoppsap1flynn1m.c1-checkpoint.ipynb deleted file mode 100644 index 927b1391..00000000 --- a/VAPs/quicklook/AOP/.ipynb_checkpoints/aoppsap1flynn1m.c1-checkpoint.ipynb +++ /dev/null @@ -1,8265 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "70840257-70e4-45e2-b491-14bff5a257a3", - "metadata": {}, - "source": [ - "# AOPPSAP1FLYNN1M.C1 Plots\n", - "\n", - "[Click here](https://www.arm.gov/capabilities/vaps/aop) for more information about this vap." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "460fd89f-e034-452c-b837-f65c5958264f", - "metadata": {}, - "outputs": [], - "source": [ - "%matplotlib widget\n", - "import ipywidgets as widgets\n", - "\n", - "import matplotlib.pyplot as plt\n", - "import ipywidgets as widgets\n", - "import numpy as np\n", - "import pandas as pd\n", - "import os\n", - "from datetime import datetime\n", - "\n", - "import act\n", - "import xarray as xr\n", - "\n", - "# Data archive directory\n", - "DATA_DIR = r'/data/archive/'\n", - "\n", - "# Datastream info\n", - "DATASTREAM_NAME = 'aoppsap1flynn1m'\n", - "DATA_LEVEL = 'c1'\n", - "LOCATIONS = [{'end_date': '2017-11-01', 'facility': 'M1', 'site': 'asi', 'start_date': '2016-04-23'}, {'end_date': '2020-06-01', 'facility': 'M1', 'site': 'anx', 'start_date': '2019-12-01'}, {'end_date': '2021-10-14', 'facility': 'M1', 'site': 'guc', 'start_date': '2021-09-02'}, {'end_date': '2023-06-15', 'facility': 'S2', 'site': 'guc', 'start_date': '2021-10-27'}, {'end_date': '2021-06-14', 'facility': 'M1', 'site': 'oli', 'start_date': '2016-08-06'}, {'end_date': '2019-04-30', 'facility': 'M1', 'site': 'cor', 'start_date': '2018-09-23'}, {'end_date': '2015-12-01', 'facility': 'S1', 'site': 'mao', 'start_date': '2014-02-06'}, {'end_date': '2018-01-11', 'facility': 'M1', 'site': 'mar', 'start_date': '2017-10-29'}, {'end_date': '2020-10-01', 'facility': 'M1', 'site': 'mos', 'start_date': '2019-10-11'}, {'end_date': '2023-12-05', 'facility': 'M1', 'site': 'epc', 'start_date': '2023-01-15'}, {'end_date': '2022-09-30', 'facility': 'M1', 'site': 'hou', 'start_date': '2021-09-08'}, {'end_date': '2023-12-11', 'facility': 'C1', 'site': 'ena', 'start_date': '2013-10-09'}, {'end_date': '2017-09-29', 'facility': 'C1', 'site': 'sgp', 'start_date': '2015-10-01'}, {'end_date': '2023-12-12', 'facility': 'E13', 'site': 'sgp', 'start_date': '2016-11-15'}]" - ] - }, - { - "cell_type": "markdown", - "id": "9faaf875", - "metadata": {}, - "source": [ - "## Define site, facility, and date range" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "ac6764f5", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "The following locations and date ranges are available for this VAP:\n" - ] - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
sitefacilitystart_dateend_date
0asiM12016-04-232017-11-01
1anxM12019-12-012020-06-01
2gucM12021-09-022021-10-14
3gucS22021-10-272023-06-15
4oliM12016-08-062021-06-14
5corM12018-09-232019-04-30
6maoS12014-02-062015-12-01
7marM12017-10-292018-01-11
8mosM12019-10-112020-10-01
9epcM12023-01-152023-12-05
10houM12021-09-082022-09-30
11enaC12013-10-092023-12-11
12sgpC12015-10-012017-09-29
13sgpE132016-11-152023-12-12
\n", - "
" - ], - "text/plain": [ - " site facility start_date end_date\n", - "0 asi M1 2016-04-23 2017-11-01\n", - "1 anx M1 2019-12-01 2020-06-01\n", - "2 guc M1 2021-09-02 2021-10-14\n", - "3 guc S2 2021-10-27 2023-06-15\n", - "4 oli M1 2016-08-06 2021-06-14\n", - "5 cor M1 2018-09-23 2019-04-30\n", - "6 mao S1 2014-02-06 2015-12-01\n", - "7 mar M1 2017-10-29 2018-01-11\n", - "8 mos M1 2019-10-11 2020-10-01\n", - "9 epc M1 2023-01-15 2023-12-05\n", - "10 hou M1 2021-09-08 2022-09-30\n", - "11 ena C1 2013-10-09 2023-12-11\n", - "12 sgp C1 2015-10-01 2017-09-29\n", - "13 sgp E13 2016-11-15 2023-12-12" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "print(\"The following locations and date ranges are available for this VAP:\")\n", - "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" - ] - }, - { - "cell_type": "markdown", - "id": "8d132223", - "metadata": {}, - "source": [ - "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "e563983a", - "metadata": {}, - "outputs": [], - "source": [ - "site_facility = ( 'sgp', 'C1' )\n", - "\n", - "date_start = '2017-09-27'\n", - "date_end = '2017-09-29'" - ] - }, - { - "cell_type": "markdown", - "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", - "metadata": {}, - "source": [ - "## Load data files\n", - "Load data files from /data/archive/" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'/data/archive/sgp/sgpaoppsap1flynn1mC1.c1'" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Compile list of files\n", - "site, facility = site_facility\n", - "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", - "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", - "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", - "dir_path\n" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "6be8f3dc", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "['20170927', '20170928', '20170929']" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "from datetime import date, timedelta\n", - "import pandas as pd\n", - "\n", - "def get_ARM_formated_dates(start_date, end_date):\n", - " \"\"\"\n", - " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", - " EXAMPLE:\n", - " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", - " >> [\"20180219\", \"20180220\", \"20180221\"] \n", - " \"\"\"\n", - " \n", - " _start_date = pd.to_datetime(start_date)\n", - " _end_date = pd.to_datetime(end_date)\n", - " \n", - " delta = _end_date - _start_date # returns timedelta \n", - " dates = []\n", - "\n", - " for i in range(delta.days + 1):\n", - " day = _start_date + timedelta(days=i)\n", - " day_formated = day.strftime(format=\"%Y%m%d\")\n", - " dates.append(day_formated)\n", - " return dates\n", - "\n", - "\n", - "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "51feea2e", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "['/data/archive/sgp/sgpaoppsap1flynn1mC1.c1/sgpaoppsap1flynn1mC1.c1.20170927.000030.nc',\n", - " '/data/archive/sgp/sgpaoppsap1flynn1mC1.c1/sgpaoppsap1flynn1mC1.c1.20170928.000030.nc',\n", - " '/data/archive/sgp/sgpaoppsap1flynn1mC1.c1/sgpaoppsap1flynn1mC1.c1.20170929.000030.nc']" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Filter a list of files based on date pattern\n", - "import glob\n", - "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", - "files_filter = []\n", - "for date in dates:\n", - " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", - " files_filter\n", - "files_filter" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "id": "b0e5d0d7", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "3 files loaded\n" - ] - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "
<xarray.Dataset>\n",
-       "Dimensions:                         (time: 4320, bound: 2)\n",
-       "Coordinates:\n",
-       "  * time                            (time) datetime64[ns] 2017-09-27T00:00:30...\n",
-       "Dimensions without coordinates: bound\n",
-       "Data variables: (12/132)\n",
-       "    base_time                       (time) datetime64[ns] 2017-09-27 ... 2017...\n",
-       "    time_offset                     (time) datetime64[ns] 2017-09-27T00:00:30...\n",
-       "    time_bounds                     (time, bound) object dask.array<chunksize=(1440, 2), meta=np.ndarray>\n",
-       "    impactor_state                  (time) int32 10 10 10 ... -9999 -9999 -9999\n",
-       "    Bs_B                            (time) float32 dask.array<chunksize=(1440,), meta=np.ndarray>\n",
-       "    qc_Bs_B                         (time) int32 dask.array<chunksize=(1440,), meta=np.ndarray>\n",
-       "    ...                              ...\n",
-       "    K1_B                            (time) float32 dask.array<chunksize=(1440,), meta=np.ndarray>\n",
-       "    K1_G                            (time) float32 dask.array<chunksize=(1440,), meta=np.ndarray>\n",
-       "    K1_R                            (time) float32 dask.array<chunksize=(1440,), meta=np.ndarray>\n",
-       "    lat                             (time) float32 36.6 36.6 36.6 ... 36.6 36.6\n",
-       "    lon                             (time) float32 -97.49 -97.49 ... -97.49\n",
-       "    alt                             (time) float32 318.0 318.0 ... 318.0 318.0\n",
-       "Attributes: (12/20)\n",
-       "    command_line:                    aosaop -n aosaoppsap -s sgp -f C1 -D -b ...\n",
-       "    Conventions:                     ARM-1.2\n",
-       "    process_version:                 vap-aosaop-1.2-0.el6\n",
-       "    dod_version:                     aoppsap1flynn1m-c1-1.2\n",
-       "    input_datastreams:               sgpaosnephdry1mC1.b1 : 1.0 : 20170927.00...\n",
-       "    site_id:                         sgp\n",
-       "    ...                              ...\n",
-       "    doi:                             10.5439/1369240\n",
-       "    history:                         created by user dsmgr on machine ruby at...\n",
-       "    _file_dates:                     ['20170927', '20170928', '20170929']\n",
-       "    _file_times:                     ['000030', '000030', '000030']\n",
-       "    _datastream:                     sgpaoppsap1flynn1mC1.c1\n",
-       "    _arm_standards_flag:             1
" - ], - "text/plain": [ - "\n", - "Dimensions: (time: 4320, bound: 2)\n", - "Coordinates:\n", - " * time (time) datetime64[ns] 2017-09-27T00:00:30...\n", - "Dimensions without coordinates: bound\n", - "Data variables: (12/132)\n", - " base_time (time) datetime64[ns] 2017-09-27 ... 2017...\n", - " time_offset (time) datetime64[ns] 2017-09-27T00:00:30...\n", - " time_bounds (time, bound) object dask.array\n", - " impactor_state (time) int32 10 10 10 ... -9999 -9999 -9999\n", - " Bs_B (time) float32 dask.array\n", - " qc_Bs_B (time) int32 dask.array\n", - " ... ...\n", - " K1_B (time) float32 dask.array\n", - " K1_G (time) float32 dask.array\n", - " K1_R (time) float32 dask.array\n", - " lat (time) float32 36.6 36.6 36.6 ... 36.6 36.6\n", - " lon (time) float32 -97.49 -97.49 ... -97.49\n", - " alt (time) float32 318.0 318.0 ... 318.0 318.0\n", - "Attributes: (12/20)\n", - " command_line: aosaop -n aosaoppsap -s sgp -f C1 -D -b ...\n", - " Conventions: ARM-1.2\n", - " process_version: vap-aosaop-1.2-0.el6\n", - " dod_version: aoppsap1flynn1m-c1-1.2\n", - " input_datastreams: sgpaosnephdry1mC1.b1 : 1.0 : 20170927.00...\n", - " site_id: sgp\n", - " ... ...\n", - " doi: 10.5439/1369240\n", - " history: created by user dsmgr on machine ruby at...\n", - " _file_dates: ['20170927', '20170928', '20170929']\n", - " _file_times: ['000030', '000030', '000030']\n", - " _datastream: sgpaoppsap1flynn1mC1.c1\n", - " _arm_standards_flag: 1" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Load files as a single dataset\n", - "files_list = files_filter \n", - "ds = act.io.armfiles.read_netcdf(files_list)\n", - "ds.clean.cleanup()\n", - "print(f'{len(files_list)} files loaded')\n", - "ds\n" - ] - }, - { - "cell_type": "markdown", - "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", - "metadata": {}, - "source": [ - "## Plot time series data\n", - "#### Define the list of variables to be plotted:" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "id": "d09b789e-84f1-4605-846b-a72c110c8048", - "metadata": {}, - "outputs": [], - "source": [ - "variables_to_plot = ['Bs_B', 'Bs_G', 'Bs_R']" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", - "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "49f2c7463c164a46aa13bd6286315713", - "version_major": 2, - "version_minor": 0 - }, - "image/png": "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SRHr168X69evF5s3bxZCCHHu3Dnx4IMPiq+//lqsWLFCLFmyRDzzzDPCz89PfPnll0IpPj5eDBs2THjiOt+hQ4cEADFjxgzHtMWLFws/Pz/RtWtXMX/+fPHDDz+Idu3aiYSEBAHAaXkAREJCgmjbtq34/vvvxaJFi0TXrl1FlSpVxIEDB4QQQhw5ckQ88cQTAoCYN2+e43vabDbN9WzYsKGoV6+e+Prrr8Xq1avF3LlzxdNPPy1WrlzpmOell14SAMSAAQPEDz/8IJYtWybeffdd8dJLLznm+eSTT0RqaqpYuHChWL16tfjyyy9F8+bNRcOGDcXly5c9rp/e9snJyREtWrQQ0dHR4t133xW//vqr+OCDD4TZbBbdu3cXBQUFTr9T7dq1RbNmzcSsWbPEihUrxM6dOx2vvfLKK455Z8yYIQCIunXriieeeEIsXbpUfP755yIyMlJ069bN6XcaMmSI8PPzE2PHjhXLli0T77//voiNjRVms9nj/pCXlye6desmqlSpIp555hmxaNEisXDhQjF+/Hgxe/ZsIYQwvB8LIcQbb7whJEkSDz30kPj555/FvHnzRPv27UVoaKj4+++/hRBC/P333+LFF1907HPr168X+/fvF5s3bxYPP/ywACCWLFki1q9fL44cOSKEEKJLly6iS5cuxVpvIdz39eJur/LYr13ZbDbH9n7xxRcdy5C/+7Bhw0RAQIBISEgQqamp4rfffhNLly5V/X4y199r5cqVju9z7733il9++UXMnj1bxMXFifr164srV6445v3888+FJEmia9euYtasWeLXX38VH3/8sRg1apRjnh9++EG8/PLLYv78+WL16tXiu+++E126dBHVq1cXp0+fFkIIcerUKfHmm28KAGLq1KmO73Xq1CnH94qPj3csszj7WXx8vKhTp45o0qSJ+Oqrr8TSpUvF3XffLQCI1atX6/7eZb3Px8fHC4vFIurWrSu++OILsXLlSvHXX385fvPatWuLu+66SyxcuFD8/PPP4syZM+Lrr78WkiSJO+64Q8ybN0/89NNP4tZbbxX+/v7i119/dSx769atIiwsTCQkJIhp06aJ3377TXzzzTdi0KBBIjs72+NvbPRzfv31V+Hv7y86deok5s2bJ3744QfRpk0bERcX53a+N6o457BHH31UABCjR48WS5YsEdOmTRPVq1cXsbGx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1atXF5IkOe1/xbnGqDG6P547d0489NBDomrVqiIkJET07NlT7NmzR/O+Iz093THN6DXmnXfeER06dBDR0dGOc8jDDz/stCwh9O8BjWwjIQqHpJPP3Q0aNBBffPGF6n6vRu9e7vDhw2Lo0KGiWrVqIiAgQDRs2FC89dZbmvujktFzkBBF19233nrLafq2bdvEoEGDRI0aNURAQICoVauW6N69u5g2bZpjntzcXPHMM8+I2rVri+DgYNGyZUuxYMECt+//5Zdfim7duomaNWuKwMBAERMTIwYNGiS2b9/u9Jl//fWX6NChgwgNDRW1a9cWr7zyivj8889VqyKrfT8hCqv5PvnkkyIxMVEEBASIqKgo0apVK/HCCy+ICxcuOOaT7xuUx8y6desMVUU2+r3l33by5Mli4sSJok6dOiIwMFCkpKQ4ht8RovC++V//+pdo1qyZiIiIECaTSTRs2FC88sorThWshSjc3+Rro9lsFv3793cagkoI/fs0+RqzatUq0bp1axEUFCQsFosYP368WxXsX3/9VaSkpIigoCCnkU3Url2lXTetc6UaSQiNHD4fl5eX56j6tmzZMm+vDulYt24dOnbsiG+//bbMqx+ScYMGDcKhQ4eQlpbm7VUhKhNt27ZFfHy8owWBiAq9+eabePHFF5GRkVHiom/e9MEHH+Df//43zp8/7+iHSESl07VrV2RmZmLnzp3eXpUS8zzGio94+OGH0bNnT0caw7Rp07B792588MEH3l41Uli+fDnWr1+PVq1awWQyYdu2bZg0aRLq16+vWYiAyp8QAqtWrXLrA0Hkq7Kzs7Ft2zZ8+eWX3l4VIq/66KOPAACNGjVCXl4eVqxYgf/85z+47777fC6otdlsWL9+PWbOnImmTZsyqCUiJ9dNYHv+/Hk888wzOH36NAICAtCyZUssWrTIqfoheV9ERASWLVuG999/H+fPn0d0dDT69OmD1NRUt/LedO1IkoRTp055ezWIykxERES5VuQn8hUhISF47733kJ6ejtzcXMTFxeH555/Hiy++6O1VK7YtW7bgzjvvRLNmzRwjURARya7bVGQiIiIiIiKqHIwPqklERERERERUATGwJSIiIiIiIp/GwJaIiIiIiIh82nVTPIqcFRQU4Pjx4wgPD4ckSd5eHSIiIiKi65oQAufPn0dMTAz8/Nh+eK0xsL1OHT9+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e+ihh9h3333HtmzZwv744w/2/PPPMxcXF/bNN9+YFKY+A9t169YxFxcXNmjQILZq1Sr2008/sV69erHo6GjRAzg6Opr17NmT/fjjj2zt2rVs0KBBzM3NzXiRyc/PZ0899RQDwNLT042/U6fTSZazQ4cOrG3btuy7775j27dvZytXrmTPPfecyQH9yiuvMABs3Lhx7KeffmIbNmxgCxYsYK+88opxmU8++YSlpaWxX3/9lW3fvp198803LCEhgXXo0IFVVFQolk9u/5SUlLCuXbuy4OBgtmDBArZp0yb2wQcfMLVazQYPHmxyUwbAwsPDWZcuXdjSpUvZli1b2JEjR4zzxALbNm3asKeeeoqtX7+effHFFywgIIAlJyebbKfJkyczFxcXlpqayjZs2MAWLlzIIiIimFqtVjweKisrWXJyMnNzc2PPP/88W7t2Lfv111/Ziy++yJYtW8YYM9wYhg8fztzc3JjcccwYY2+99RbjOI7961//YmvWrGHp6emsT58+zMfHhx09epQxxtjRo0fZyy+/bDzmdu3axU6fPs3279/PHn74YQaA/fHHH2zXrl3GE878JmRNuRkTv5nasr8a4rg2p9PpjPv75ZdfNq6D/+1Tp05l7u7uLDo6mqWlpbHNmzez9evXi/4+nlRgGx0dze677z72+++/s2XLlrHIyEjWrl07dvPmTeOyX3zxBeM4jg0aNIgtXbqUbdq0iX388ccmD6Y//fQTe/XVV9mqVavY9u3b2fLly9nAgQNZy5YtjS+MLl26xN5++20GgH300UfG33Xp0iXj7xLeJG05zqKioljr1q1Zp06d2LfffsvWr1/P7r33XgaAbd++XXZ71/cxHxUVxTQaDWvTpg376quv2NatW9mePXuM2zw8PJzdc8897Ndff2Vr1qxhV65cYd999x3jOI6NHTuWpaens99++42NHj2aubq6mjyYHDx4kLVo0YJFR0ezTz/9lG3evJl9//33bMKECay4uFhxG1v7PZs2bWKurq6sf//+LD09nf30008sKSmJRUZG1jmwteYa9thjjzEA7Mknn2R//PEH+/TTT1nLli1ZRESEyQvIgQMHsqCgIONLjPXr17PRo0czAGzu3LksPj6eLVu2jK1du5b17t2beXp6mgTRUoFtUFAQa9euHfv000/Zxo0b2YwZMxgAk3tsfd+Lq6qqWP/+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358NHDiQnTx50mltq4Qrzy2OmfPmzWONGzdm3t7erFmzZmzx4sWycrm5ufZ6+/j4sKCgINa+fXu2aNEiWblVq1axzp07M4PBwGrXrs369evHkpOTZWXE9rdv3z42cuRIFhgYyMLDwxljxeuzo0ePsj59+jA/Pz8WEhLCJk+ezHJzc2X1dvwn9j2lb+Jq3fSMa47Mnj2bCYLALl++bD/24YcfMpw+fZo99NBDDADbuXOn/V9eXh4rKipiAwcOZLVq1WJvvvkmW79+Pfvwww+Zv78/a9u2rb1D7d+/nzVs2JC1bdvW/ntREMjJyWFPPvkk++9//8s2b97MfvvtN/bqq68yDw8P9u2338oq6U7Bdu3atczDw4P16tWLrVixgi1dupR16tSJxcTEKAq2MTExrGPHjuynn35ia9asYb169WK1atViaWlpjDHGMjMz2fPPP88AsOXLl9uf02KxcOvZtGlT1qhRI/bf//6Xbdu2jS1btoy98sorso//5ptvMgBsxIgRbOnSpWz9+vXs448/Zm+++aa9zOeff86SkpLY6tWr2bZt29i3337LWrduzZo2bcry8/M166f2fXJzc1mbNm1YSEgI+/jjj9nGjRvZp59+yoxGI+vTp49sEgLA6tevz1q1asUWLVrENm/ezI4ePWo/pyTYNmzYkD3//PNs3bp17Msvv2RBQUGsd+/esvc0ZswY5uHhwaZOncrWr1/PPvnkExYZGcmMRqNmeygoKGC9e/dmtWrVYq+++ipbs2YNW716NXv99dftA4XedswYY++++y4TBIE99dRT7JdffmHLly9nXbp0Yf7+/uzYsWOMMcaOHTvG3njjDXub27lzJzt9+jTbv38/++tf/8oAsN9++43t3LnTvrB1nID11Jsx57bu6vcqi3btiMVisX/vN954w34N8dnHjRvHvLy8WExMDEtKSmKbNm1i69atU3w+EZ5gGxMTwx599FH266+/ssWLF7OoqCjWuHFjdvfuXXvZL7/8kgmCwHr16sUWLVrENm7cyObOncsmTZpkL7N06VL2z3/+k61YsYJt27aN/fjjj6xnz54sNDTUvli9cuUKe++99xgANmfOHPtzXblyxf5c0sWTK+0sOjqaNWjQgDVv3px99913bN26dezhhx9mANi2bdtU37e723x0dDQzmUysYcOG7Ouvv2Zbtmxhe/bssb/z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VomeRdqneFgsFvzyyy946623MHXqVPvxO3fucBegenDHeKkHd7Z5EbV+7XhO/C6fffYZNxJivXr1UFhYCE9PT9VvoYbe+4iRmpXG9vIY78UxzGw2OwnxFy9erDR9sqzmYsf2ERQUBE9PTzz++ONca6TY2FgAtndXmu/WoEEDNGnSBBs3bkRMTAw6dOiAwMBA9O3bF5MmTcLu3buxa9cuWTrDzZs34+LFi9i6datdSwtAMeWN+Bz/+c9/kJOTg0WLFuHOnTsYP368vYzedqpEaX7LQ+z7169flwkt5bn2+fHHH+Hl5YVffvlFpr10TBUi9p1Lly6VaP739fWFxWJxOq60CT1+/HiMHz8eubm52L59O9566y0MGzYMf/75p+p8XxnGobp162L37t1gjMn625UrV3D37t0yHWNc6c+ujDEPPPAAHnjgAdy5cwe7du1CUlISxo4di5iYGHTp0sXebhx/y1MwKM1hetdLYp+5du2aTMDS+91cHVvV2oh4f39/f8yYMQMzZszA5cuX7drbv/zlLzhx4oRs3nHk4sWL8PDwcLKu5M3zas9ekg2mktStpPTt2xeAbQNzw4YN6N+/v01jy5OMxR98//33suPLli1Dbm6u/bx4DSXJWhAEeHt7y17opUuXsGrVKnc8kyL+/v7o0KEDVq5cifz8fPvxW7du4ZdffinxdfXuICjRpEkTvPHGG0hISMD+/fsBAAMGDICnpyc+//xz7u/E9ybeW2T+/Pku1Y/3fYYNG4a0tDTUrVsXHTp0cPoXExOj+xlLQo8ePQAAS5YskR3/3//+h7t372r+fvDgwcjLy1PN0au3HQ8cOBC1atVCWlqa4rvo0KGDK49W6norURbfqzTturTXiImJweHDh2XH/vzzT5w8ebJE9ejatSuMRiPmzZsHxphiGUEQwBhz6lNffvklCgsLZcdceS5XxsvS4M42XxK6deuGwMBApKamcvuJt7c3DAYDevbsiaVLl6paO/Desd77+Pv7o2PHjli+fLlsd/zmzZv4+eefS/ycehHN3xzfdUpKCo4fP+62715aymsu9vPzQ+/evXHgwAG0atVK8buJC5/evXvj2LFjOHTokOwaixYt0n2/fv36YfPmzfZFDWCbb6OiovDPf/4TBQUFds0u4NqcCtgEory8PCxevBjffPMNunTpgvj4ePt5ve1UidL8loco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szOllNItt9ySpk6d2u15X//619NnP/vZv/0E6NeMGTPS9ddf323b1KlT05IlS1JnZ2eqq6tLd955Z37f3r17U3V1dbrvvvv6LFNbFzd9+sSmT5cm/RqKm6nIkBGtra2Ry+Vi5MiR+W0LFy6M2bNn5x+/+OKLMWvWrG6LwM+bNy927NgRTU1N+WPmzp3brex58+bFq6++Gh0dHQN5CiVp3759sX79+h6/87lz58YLL7wQW7dujebm5m77y8vLY9asWfHCCy/kt2nrE48+nU36NP3Rr6FwBFvIgL1798aSJUvimmuuiaqqqvz2MWPGxPjx4/OPm5ubo7a2tttzux43Nzf3e8z+/fujpaVloE6hZLW0tMSBAwd6/Z03Nzfn26Wv/V209YlFn84ufZq+6NdQWCcVugJA/zo6OuLqq6+Ozs7OWL58ebd9y5Yt63F8Lpfr9jj99WYUB28/kmM4tnr7nR+uTQ7epq1PHPr0iUGf5mD6NRSeEVsoYh0dHXHllVfG1q1bY9WqVd2+Ae5NXV1dtxGBiIh33303Ij7+NrivY0466aQYNWrUMaw9ERGjR4+OwYMH9/o7r62tjbq6uoiIPvf3RVtnkz6dffo0h9KvoTgItlCkut4oN2/eHM8888wRvZHNnDkzGhoaYt++ffltTz/9dNTX18eECRPyx6xatarb855++uk477zzoqys7JieAxFDhgyJ6dOn9/idr1q1Ki644IKYOHFi1NXVddu/b9++WLNmTVxwwQV9lquts0efPjHo0xxMv4YiUog7VgEp7d69O23YsCFt2LAhRUS666670oYNG9K2bdtSR0dH+tKXvpTGjh2bfvvb36adO3fmfz788MN8GUuWLElf/epX84937dqVamtr04IFC1JjY2NauXJlqqqq6nUJgW984xtp48aN6YEHHrCEwADrWhrkgQceSBs3bkw33XRTGj58eGpqakopfbQ0SHV1dVq5cmVqbGxMCxYs6LE0iLYufvp06dCnS4d+Ddkh2EKBPPfccykievxce+21aevWrb3ui4j03HPP5cu49tpr06xZs7qV+/vf/z59/vOfT+Xl5amuri4tXbo0v3xAl9WrV6dPf/rTaciQIWnChAnp3nvvPQ5nXNruueee9IlPfCINGTIknXvuuWnNmjX5fZ2dnen2229PdXV1qby8PF144YWpsbGx2/O1dfHTp0uLPl0a9GvIjlxKf70SHQAAADLINbYAAABkmmALAABApgm2AAAAZJpgCwAAQKYJtgAAAGSaYAsAAECmCbYAAABkmmALAH1YunRpfOpTnzrur7t69erI5XKRy+XisssuO+6v32XChAn5euzatatg9QCAwxFsAShJXYGtr5+FCxfGt7/97Xj22WcLVsdNmzbFQw89lH88e/bsuOmmm3oc98QTT0Qul8sf0995TZgwISIimpubY/HixTFp0qQoLy+PcePGxaWXXtrtfF955ZVYsWLFQJ4iABwTJxW6AgBQCDt37sz/+7HHHovvfe97sWnTpvy2ioqKGDFiRIwYMaIQ1YuIiFNPPTVGjhx5VM9ZuXJl7Nu3LyIitm/fHjNmzIhnnnkmpk2bFhERgwcPjqampvjc5z4XI0eOjB/+8Idx9tlnR0dHR/zyl7+MRYsWxeuvvx4REaecckrU1NQc03MCgIFgxBaAklRXV5f/qa6ujlwu12PboVORFy5cGJdddln84Ac/iNra2hg5cmTccccdsX///rj55pujpqYmxo4dGz/5yU+6vdY777wTV111VZx88skxatSomD9/fjQ1NQ3IedXU1OTP4ZRTTomIiFGjRnXbdsMNN0Qul4uXX345vvzlL8fpp58e06ZNi29+85uxbt26AakXAAwkwRYAjsKvfvWr2LFjRzQ0NMRdd90VS5cujUsuuSROPvnkeOmll+L666+P66+/PrZv3x4REe3t7TFnzpwYMWJENDQ0xPPPPx8jRoyIL37xi/mR1ePpvffei6eeeioWLVoUw4cP77H/aEeIAaAYCLYAcBRqamri7rvvjilTpsR1110XU6ZMifb29rjtttti8uTJceutt8aQIUPi17/+dUREPProozFo0KD48Y9/HGeddVacccYZ8eCDD8Zbb70Vq1evPu71f+ONNyKlFFOnTj3urw0AA8U1tgBwFKZNmxaDBn38vXBtbW2ceeaZ+ceDBw+OUaNGxbvvvhsREevXr4833ngjKisru5Wzd+/e2LJly/Gp9EFSShER+ZtNAcCJQLAFgKNQVlbW7XEul+t1W2dnZ0REdHZ2xvTp0+OnP/1pj7K6roE9UlVVVdHa2tpj+65du6KqquqIypg8eXLkcrl47bXXCrqUEAAcS6YiA8AAOvfcc2Pz5s1x6qmnxic/+cluP9XV1UdV1tSpU+PVV1/tsf2VV16JKVOmHFEZNTU1MW/evLjnnnvigw8+6LHferUAZJFgCwAD6Ctf+UqMHj065s+fH2vXro2tW7fGmjVr4sYbb4y33377qMq64YYbYsuWLbFo0aL43e9+F3/605/innvuiQceeCBuvvnmIy5n+fLlceDAgZgxY0asWLEiNm/eHK+99lrcfffdMXPmzKM9RQAoOMEWAAbQsGHDoqGhIcaPHx+XX355nHHGGXHdddfFnj17jnj6cJcJEybE2rVrY8uWLTF37tw4//zz46GHHoqHHnoorrjiiiMuZ+LEifGb3/wm5syZE9/61rfizDPPjIsuuiieffbZuPfee4/2FAGg4HKp6y4SAEBRWL16dcyZMyfef//9gi+/U0x1AYC+GLEFgCI1duzYWLBgQcFef9q0aXHxxRcX7PUB4EgZsQWAIrNnz5545513IiJixIgRUVdXV5B6bNu2LTo6OiIiYtKkSd2WOQKAYiLYAgAAkGm+egUAACDTBFsAAAAyTbAFAAAg0wRbAAAAMk2wBQAAINMEWwAAADJNsAUAACDTBFsAAAAyTbAFAAAg0wRbAAAAMk2wBQAAINMEWwAAADJNsAUAACDTBFsAAAAyTbAFAAAg0wRbAAAAMk2wBQAAINMEWwAAADJNsAUAACDTBFsAAAAyTbAFAAAg0wRbAAAAMk2wBQAAINMEWwAAADJNsAUAACDTBFsAAAAyTbAFAAAg0wRbAAAAMk2wBQAAINMEWwAAADJNsAUAACDTBFsAAAAyTbAFAAAg0wRbAAAAMk2wBQAAINMEWwAAADJNsAUAACDTBFsAAAAyTbAFAAAg0wRbAAAAMk2wBQAAINMEWwAAADJNsAUAACDTBFsAAAAyTbAFAAAg0wRbAAAAMk2wBQAAINMEWwAAADJNsAUAACDTBFsAAAAyTbAFAAAg0wRbAAAAMk2wBQAAINMEWwAAADJNsAUAACDTBFsAAAAyTbAFAAAg0wRbAAAAMk2wBQAAINMEWwAAADJNsAUAACDTBFsAAAAyTbAFAAAg0wRbAAAAMk2wBQAAINMEWwAAADJNsAUAACDTBFsAAAAyTbAFAAAg0wRbAAAAMk2wBQAAINMEWwAAADJNsAUAACDTBFsAAAAyTbAFAAAg0wRbAAAAMk2wBQAAINMEWwAAADJNsAUAACDTBFsAAAAyTbAFAAAg0wRbAAAAMk2wBQAAINMEWwAAADJNsAUAACDTBFsAAAAyTbAFAAAg0wRbAAAAMu3/A4Sl4jH6Fvp7AAAAAElFTkSuQmCC", - "text/html": [ - "\n", - "
\n", - "
\n", - " Figure\n", - "
\n", - " \n", - "
\n", - " " - ], - "text/plain": [ - "Canvas(toolbar=Toolbar(toolitems=[('Home', 'Reset original view', 'home', 'home'), ('Back', 'Back to previous …" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", - "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", - "\n", - "for i,v in enumerate(variables_to_plot):\n", - " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", - " ts_ax.grid()\n", - "\n", - "plt.show()\n" - ] - }, - { - "cell_type": "markdown", - "id": "194399aa-1907-452b-8ba9-bc31d7f60291", - "metadata": {}, - "source": [ - "## Quality check plots\n", - "#### Define variable for QC plot" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", - "metadata": {}, - "outputs": [], - "source": [ - "qc_variable = 'Bs_B'" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/home/kefeimo/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/act/utils/datetime_utils.py:136: FutureWarning: Unlike other reduction functions (e.g. `skew`, `kurtosis`), the default behavior of `mode` typically preserves the axis it acts along. In SciPy 1.11.0, this behavior will change: the default value of `keepdims` will become False, the `axis` over which the statistic is taken will be eliminated, and the value None will no longer be accepted. Set `keepdims` to True or False to avoid this warning.\n", - " mode = stats.mode(np.diff(time))\n" - ] - }, - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "1b8f0eadc61146b1b9944c7cadc77104", - "version_major": 2, - "version_minor": 0 - }, - "image/png": "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PtzwvLfccovau3dvG+8yUFZWlgpA/fbbbwNuf/jhh1VFUdR9+/YFrF9GRoZ66dIl/3IbN25UAajvv/++6etoj//DH/4QcPu7776rAlBzc3NVVVXV4uJitWXLluqtt94asFx5ebmamZmpXnPNNdLHFhUVqY0bN1YnTJgQ8Nivv/5aBWD43K6//no1NjbWdL316y767/777w/4POzsA3Zo+2Lwfy6XS123bl21npuIiOoGU5GJiMiSenmULbhqcE2YNGlSwN+ffPIJWrRogVtvvRWXLl3y/9e7d2+0a9fOn1bcu3dvxMfH46GHHsI///lPHDp0qMbXTeaaa67B9u3bMW3aNHz++ec4e/asrcetWLECPXr0wDXXXBNw+/333+8fCdcbN24cYmNj/X/36tULAHD48OFqvgOf9evX4/Tp07jvvvsCPuuKigrcfPPNyMnJQXFxsfCxGzZswIULF/CjH/0o4PbBgwejY8eOhuW/+uor/4ioHY8//jhycnKQk5ODlStX4sUXX8SHH36Ie+65x79MTe4DDofD/3rffvstFi5ciK5du2Ls2LHYsGFDlZ+XiIjqBlORiYgasOTkZCQmJiI3N9d0uby8PDgcDrRq1Qrl5eW2HmNHYmIimjdvHnDb8ePHcebMGcTHxwsfc+rUKQBAly5d8OWXX2LOnDl45JFHUFxcjM6dO+MXv/gFHn/88Wqvm5kZM2agSZMmeO+99/DGG28gNjYWw4YNw+zZs9G/f3/p43744Qf/fFe9lJQU//16rVq1CvhbSyn2er3VfAc+x48fBwDccccd0mVOnz6NJk2aGG7X1rVdu3aG+0S3hapDhw4Bn+WIESOgKApmzJiBzz//HKNHj67RfSAmJsaw7UaPHo3U1FQ88cQTDG6JiCIcA1siogYsNjYW119/PZYuXYojR44I59keOXIEmzdvxs033+x/zA033GD6GLtEI8DJyclo1aqVtKpys2bN/P++7rrrcN1116G8vBybNm3yty1q27Yt7r77bjRu3BgAUFpaGvAcWnBcVY0aNcITTzyBJ554AmfOnMGXX36Jp59+GqNHj0ZBQYF0bmerVq3gdrsNtx89ehSA773XJe31XnnllYCKwHpt27YV3q4F3ceOHTPcd+zYMWEAX13aiPX27dsxevRoANb7QHUkJiaiS5cu2L59e7XXnYiIahdTkYmIGris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sQ0mw3xBxLqnW7/bYYa98dPX+vGSC/0nm/sudM9N81keSIpxH7bvDcF/mO2FeizOj71dgInDzT3sem6HEF6KS/g27f0CoSqYucfCxuER64yL+H8o4dWenUaZn2HWs3llKYzIJ7wqdqRjiSrJ6v312nMcOidu98q5hHeYBbGfoQ7mnA0vtNLltt04z79zm57L2zUIOnnM93MiHH1axP0Wi9vxuH0eRTy7/W6I2H48n0WFmGG8nI6O+P9cGHpx7TFsqST256eHAd9z3BdgAuJp0csddzmsdNQQiDo2zD0WNi0XM3yaJSvN5bnsClxr/hyaBo9G3qPgej/b43qLiIjEosDWx7XXXsu1114bNm3ChAlMmDDBdfkLLriACy64wHN7xx13HEuWLIlr37/4xS/4xS9+EXtBEZEjVSqmFoiIiIhIDApsRUSkZfKrASAiIiLioF6RRUREREREJKEpsBUREREREZGEpsBWREREREREEpoCWxEREREREUloCmxFREREREQkoSmwFRERERERkYSmwFZEREREREQSmgJbERERERERSWgKbEVERERERCShKbAVERERERGRhKbAVkRERERERBKaAlsRERERERFJaApsRUREREREJKEpsBUREREREZGEpsBWREREREREEpoCWxEREREREUloCmxFREREREQkoSmwFRERERERkYTWprkTII1ryyH44y4Ylg6fHaydXnoIfr0TCJi/66P0EEzb4b1+rPktRX3T2djHl+jbP5x9NvRyDbWec337+fnsYMOeRztt9d1uS7pvPjsIH+yHJQcb/jzFoyHPhdu2muIZaqh9xNrOZwfhnf3m99Zq9++Hhnoum+Me3VMT/Xc86Tjc59G5348OhH8Px5OGxjxXDZEPqM8+m+M5EpHWIRAMBoPNnQhpeOXl5WRnZ8MUIL25UyPSshy1DZ6aA1deBKs6N3dqRKQp9E0xv7+oavr99t4JM57SZ47IEe8AMBPKysrIyspq7tS0OiqxFZFWJ7MKRpaa3yLSOjR1QOvcb/s9+swREWlsamMrIiIiIiIiCU2BrYiIiIiIiCQ0BbYiIiIiIiKS0BTYioiIiIiISEJTYCsiIiIiIiIJTYGtiIiIiIiIJDQFtiIiIiIiIpLQFNiKiIiIiIhIQlNgRzVgHAAAdLVJREFUKyIiIiIiIglNga2IiIiIiIgkNAW2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YQoNnxE4iBnvcALh4+GKJIgRm+HTR2HkDbCoPsGSNK2XMdW6z23uhIjUjz5zRFqJA8BMKCsr8649KY1Gge0RqkED21JMtbHumAf2XUwJws3Ur52nSDNTJjPBfIKpJt411oIiLZM+c0RaCQW2zUpVkSU+H2CC22RMdbJvoqBWRJrG6OZOgIiIiLR0CmwltnzM8CQiIiIiIiItkHpFFhERERERkYSmwFZEREREREQSmgJbERERERERSWgKbEVERERERCShKbAVERERERGRhKbAVkRERERERBKaAlsRERERERFJaApsRUREREREJKEpsBUREREREZGEpsBWREREREREEpoCWxEREREREUloCmxFREREREQkoSmwFRERERERkYSmwFZEREREREQSmgJbERERERERSWgKbEVERERERCShKbAVERERERGRhKbAVkRandL2MO1k81tEpLHpM0dEpPEFgsFgsLkTIQ2vvLyc7OxsmAKk+y97VAo8lRffdq/cAquqmnZ7jaEuaYxXvMfSnPuOV7xpbK79Nsa+WyOd76bVnOdb1zrxNdfnsojUwQFgJpSVlZGVldXcqWl12jR3AqT5ZSbByBjBr3PZpt5eY6hLGuuyzZa+77psL540Ntd+G2PfrZHOd9NqzvOta534mutzWUQkUejjT0RERERERBKaAlsRERERERFJaApsRUREREREJKEpsBUREREREZGEpsBWREREREREEpoCWxEREREREUloCmxFREREREQkoSmwFRERERERkYSmwFZEREREREQSmgJbERERERERSWgKbEVERERERCShKbAVERERERGRhKbAVkRERERERBKaAlsRERERERFJaApsRUREREREJKEpsBWRuDz6FhT8HyRdDQ++AtOehZW/q8MGdgHTgFLvRfashcBVsHvf4aW1RXob+BXmHKwAngP+2ZwJqpvP7gPmN2MCFgEzmnH/9TUNc71FRESkUbVp7gRIM6gBPsJkFHfA0hQ4uz/87AI44ajmTZq0TOX74Xuz4P6r4eIxkJ0JNUGYMwiKmztxiWAb8C5wGdADyABWNmuKGt7bwG7gQuAB4AKglzVvGubYBzZxmpprv04/xFzvhvQEkAecXbfVrv0TFHWGaRebF0jrHjT/i4iIHAlUYhshEAj4/lx77bX13nZRUREPPvhgzOXGjx8ftd/LL7+83vsN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", - "text/html": [ - "\n", - "
\n", - "
\n", - " Figure\n", - "
\n", - " \n", - "
\n", - " " - ], - "text/plain": [ - "Canvas(toolbar=Toolbar(toolitems=[('Home', 'Reset original view', 'home', 'home'), ('Back', 'Back to previous …" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# QC Plot\n", - "if ('qc_'+qc_variable) in ds.variables:\n", - "\n", - " # Plot\n", - " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", - " qc_display.add_subplots((2,), figsize = (9.5,10))\n", - " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", - " qc_ax.grid()\n", - " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", - "\n", - " plt.show()\n", - "else:\n", - " print(f'QC not available for the selected field: {qc_variable}')\n" - ] - }, - { - "cell_type": "markdown", - "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", - "metadata": {}, - "source": [ - "## Field selection dropdown menu\n", - "Select variable to be plotted from a dropdown menu" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", - "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "3d3082e5808440dc89d3713bcdf41d09", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "AppLayout(children=(Dropdown(description='Field:', index=1, layout=Layout(grid_area='header', margin='0px 30% …" - ] - }, - "execution_count": 13, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "plt.ioff()\n", - "\n", - "# populate dropdown menu with available variables \n", - "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", - "d_variable = 'Bs_B'\n", - "dropdown = widgets.Dropdown(\n", - " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", - " value= d_variable,\n", - " description='Field:',\n", - " disabled=False,\n", - ")\n", - "dropdown.layout.margin = '0px 30% 0px 20%'\n", - "dropdown.layout.width = '50%'\n", - "\n", - "# set up display\n", - "i_display = act.plotting.TimeSeriesDisplay(ds)\n", - "i_display.add_subplots((1,), figsize = (9.5,5))\n", - "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", - "i_ax.grid()\n", - "i_fig = i_display.fig\n", - "\n", - "# update plot callback function\n", - "def update_plot(change):\n", - " i_ax.cla()\n", - " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", - " i_ax_new.grid()\n", - " i_fig.canvas.draw()\n", - " i_fig.canvas.flush_events()\n", - "\n", - "dropdown.observe(update_plot, names='value')\n", - "\n", - "widgets.AppLayout(\n", - " header=dropdown,\n", - " center=i_fig.canvas,\n", - " pane_heights=[1, 6,1]\n", - ")\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "3c0aba93", - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.16" - }, - "widgets": { - "application/vnd.jupyter.widget-state+json": { - "state": {}, - "version_major": 2, - "version_minor": 0 - } - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/VAPs/quicklook/ARSCL/.ipynb_checkpoints/arscl1cloth.c1-checkpoint.ipynb b/VAPs/quicklook/ARSCL/.ipynb_checkpoints/arscl1cloth.c1-checkpoint.ipynb deleted file mode 100644 index 4502aea5..00000000 --- a/VAPs/quicklook/ARSCL/.ipynb_checkpoints/arscl1cloth.c1-checkpoint.ipynb +++ /dev/null @@ -1,2631 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "70840257-70e4-45e2-b491-14bff5a257a3", - "metadata": {}, - "source": [ - "# ARSCL1CLOTH.C1 Plots\n", - "\n", - "[Click here](https://www.arm.gov/capabilities/vaps/arscl) for more information about this vap." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "460fd89f-e034-452c-b837-f65c5958264f", - "metadata": {}, - "outputs": [], - "source": [ - "%matplotlib widget\n", - "import ipywidgets as widgets\n", - "\n", - "import matplotlib.pyplot as plt\n", - "import ipywidgets as widgets\n", - "import numpy as np\n", - "import pandas as pd\n", - "import os\n", - "from datetime import datetime\n", - "\n", - "import act\n", - "import xarray as xr\n", - "\n", - "# Data archive directory\n", - "DATA_DIR = r'/data/archive/'\n", - "\n", - "# Datastream info\n", - "DATASTREAM_NAME = 'arscl1cloth'\n", - "DATA_LEVEL = 'c1'\n", - "LOCATIONS = [{'end_date': '2011-03-23', 'facility': 'C1', 'site': 'nsa', 'start_date': '1998-03-25'}, {'end_date': '2011-01-04', 'facility': 'C1', 'site': 'sgp', 'start_date': '1996-11-08'}, {'end_date': '2011-03-07', 'facility': 'C1', 'site': 'twp', 'start_date': '1999-07-01'}, {'end_date': '2009-02-14', 'facility': 'C2', 'site': 'twp', 'start_date': '1998-11-01'}, {'end_date': '2011-02-28', 'facility': 'C3', 'site': 'twp', 'start_date': '2003-01-01'}]" - ] - }, - { - "cell_type": "markdown", - "id": "9faaf875", - "metadata": {}, - "source": [ - "## Define site, facility, and date range" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "ac6764f5", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "The following locations and date ranges are available for this VAP:\n" - ] - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
sitefacilitystart_dateend_date
0nsaC11998-03-252011-03-23
1sgpC11996-11-082011-01-04
2twpC11999-07-012011-03-07
3twpC21998-11-012009-02-14
4twpC32003-01-012011-02-28
\n", - "
" - ], - "text/plain": [ - " site facility start_date end_date\n", - "0 nsa C1 1998-03-25 2011-03-23\n", - "1 sgp C1 1996-11-08 2011-01-04\n", - "2 twp C1 1999-07-01 2011-03-07\n", - "3 twp C2 1998-11-01 2009-02-14\n", - "4 twp C3 2003-01-01 2011-02-28" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "print(\"The following locations and date ranges are available for this VAP:\")\n", - "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" - ] - }, - { - "cell_type": "markdown", - "id": "8d132223", - "metadata": {}, - "source": [ - "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "e563983a", - "metadata": {}, - "outputs": [], - "source": [ - "site_facility = ( 'sgp', 'C1' )\n", - "\n", - "date_start = '2011-01-03'\n", - "date_end = '2011-01-04'" - ] - }, - { - "cell_type": "markdown", - "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", - "metadata": {}, - "source": [ - "## Load data files\n", - "Load data files from /data/archive/" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'/data/archive/sgp/sgparscl1clothC1.c1'" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Compile list of files\n", - "site, facility = site_facility\n", - "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", - "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", - "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", - "dir_path\n" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "6be8f3dc", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "['20110103', '20110104']" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "from datetime import date, timedelta\n", - "import pandas as pd\n", - "\n", - "def get_ARM_formated_dates(start_date, end_date):\n", - " \"\"\"\n", - " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", - " EXAMPLE:\n", - " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", - " >> [\"20180219\", \"20180220\", \"20180221\"] \n", - " \"\"\"\n", - " \n", - " _start_date = pd.to_datetime(start_date)\n", - " _end_date = pd.to_datetime(end_date)\n", - " \n", - " delta = _end_date - _start_date # returns timedelta \n", - " dates = []\n", - "\n", - " for i in range(delta.days + 1):\n", - " day = _start_date + timedelta(days=i)\n", - " day_formated = day.strftime(format=\"%Y%m%d\")\n", - " dates.append(day_formated)\n", - " return dates\n", - "\n", - "\n", - "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "51feea2e", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "['/data/archive/sgp/sgparscl1clothC1.c1/sgparscl1clothC1.c1.20110103.000000.cdf',\n", - " '/data/archive/sgp/sgparscl1clothC1.c1/sgparscl1clothC1.c1.20110104.000000.cdf']" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Filter a list of files based on date pattern\n", - "import glob\n", - "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", - "files_filter = []\n", - "for date in dates:\n", - " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", - " files_filter\n", - "files_filter" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "id": "b0e5d0d7", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "77 files loaded\n" - ] - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "
<xarray.Dataset>\n",
-       "Dimensions:                         (time: 8640, nheights: 512, numlayers: 10)\n",
-       "Coordinates:\n",
-       "  * time                            (time) timedelta64[ns] 00:00:00 ... 23:59:50\n",
-       "Dimensions without coordinates: nheights, numlayers\n",
-       "Data variables: (12/23)\n",
-       "    base_time                       object ...\n",
-       "    time_offset                     (time) timedelta64[ns] dask.array<chunksize=(8640,), meta=np.ndarray>\n",
-       "    Heights                         (nheights) float32 dask.array<chunksize=(512,), meta=np.ndarray>\n",
-       "    Reflectivity                    (time, nheights) int16 dask.array<chunksize=(8640, 512), meta=np.ndarray>\n",
-       "    ReflectivityNoClutter           (time, nheights) int16 dask.array<chunksize=(8640, 512), meta=np.ndarray>\n",
-       "    ReflectivityBestEstimate        (time, nheights) int16 dask.array<chunksize=(8640, 512), meta=np.ndarray>\n",
-       "    ...                              ...\n",
-       "    CloudLayerBottomHeightMplZwang  (time, numlayers) float32 dask.array<chunksize=(8640, 10), meta=np.ndarray>\n",
-       "    CloudLayerTopHeightMplZwang     (time, numlayers) float32 dask.array<chunksize=(8640, 10), meta=np.ndarray>\n",
-       "    qc_RadarArtifacts               (time, nheights) |S1 dask.array<chunksize=(8640, 512), meta=np.ndarray>\n",
-       "    qc_ReflectivityClutterFlag      (time, nheights) |S1 dask.array<chunksize=(8640, 512), meta=np.ndarray>\n",
-       "    qc_CloudLayerTopHeightMplZwang  (time, numlayers) float32 dask.array<chunksize=(8640, 10), meta=np.ndarray>\n",
-       "    qc_BeamAttenuationMplZwang      (time) float32 dask.array<chunksize=(8640,), meta=np.ndarray>\n",
-       "Attributes: (12/18)\n",
-       "    Date:                      Wed Jul 13 16:28:55 GMT 2011\n",
-       "    Version:                   $State: Exp $\n",
-       "    Number_Input_Platforms:    3\n",
-       "    Input_Platforms:           sgp30smplcmask1zwangC1.c1,sgpvceil25kC1.b1,sgp...\n",
-       "    Input_Platforms_Versions:  ?????,10.2,1.16\n",
-       "    Command_Line:              arsc1/arscl2 -s YYYYMMDD -e YYYYMMDD SITE FACI...\n",
-       "    ...                        ...\n",
-       "    commentf:                  Note that -32768 is also used for the geophysi...\n",
-       "    _file_dates:               ['20110103']\n",
-       "    _file_times:               ['000000']\n",
-       "    datastream:                sgparscl1clothC1.c1\n",
-       "    _datastream:               sgparscl1clothC1.c1\n",
-       "    _arm_standards_flag:       1
" - ], - "text/plain": [ - "\n", - "Dimensions: (time: 8640, nheights: 512, numlayers: 10)\n", - "Coordinates:\n", - " * time (time) timedelta64[ns] 00:00:00 ... 23:59:50\n", - "Dimensions without coordinates: nheights, numlayers\n", - "Data variables: (12/23)\n", - " base_time object ...\n", - " time_offset (time) timedelta64[ns] dask.array\n", - " Heights (nheights) float32 dask.array\n", - " Reflectivity (time, nheights) int16 dask.array\n", - " ReflectivityNoClutter (time, nheights) int16 dask.array\n", - " ReflectivityBestEstimate (time, nheights) int16 dask.array\n", - " ... ...\n", - " CloudLayerBottomHeightMplZwang (time, numlayers) float32 dask.array\n", - " CloudLayerTopHeightMplZwang (time, numlayers) float32 dask.array\n", - " qc_RadarArtifacts (time, nheights) |S1 dask.array\n", - " qc_ReflectivityClutterFlag (time, nheights) |S1 dask.array\n", - " qc_CloudLayerTopHeightMplZwang (time, numlayers) float32 dask.array\n", - " qc_BeamAttenuationMplZwang (time) float32 dask.array\n", - "Attributes: (12/18)\n", - " Date: Wed Jul 13 16:28:55 GMT 2011\n", - " Version: $State: Exp $\n", - " Number_Input_Platforms: 3\n", - " Input_Platforms: sgp30smplcmask1zwangC1.c1,sgpvceil25kC1.b1,sgp...\n", - " Input_Platforms_Versions: ?????,10.2,1.16\n", - " Command_Line: arsc1/arscl2 -s YYYYMMDD -e YYYYMMDD SITE FACI...\n", - " ... ...\n", - " commentf: Note that -32768 is also used for the geophysi...\n", - " _file_dates: ['20110103']\n", - " _file_times: ['000000']\n", - " datastream: sgparscl1clothC1.c1\n", - " _datastream: sgparscl1clothC1.c1\n", - " _arm_standards_flag: 1" - ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Load files as a single dataset\n", - "files_list = files_filter[0]\n", - "ds = act.io.armfiles.read_netcdf(files_list)\n", - "ds.clean.cleanup()\n", - "print(f'{len(files_list)} files loaded')\n", - "ds\n" - ] - }, - { - "cell_type": "markdown", - "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", - "metadata": {}, - "source": [ - "## Plot time series data\n", - "#### Define the list of variables to be plotted:" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "id": "d09b789e-84f1-4605-846b-a72c110c8048", - "metadata": {}, - "outputs": [], - "source": [ - "variables_to_plot = ['Reflectivity', 'ReflectivityNoClutter', 'ReflectivityBestEstimate']" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "id": "de5b8b3d", - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "
<xarray.DataArray 'ReflectivityNoClutter' (time: 8640, nheights: 512)>\n",
-       "dask.array<open_dataset-86c2f04dd636517ce84998ed679d9bf5ReflectivityNoClutter, shape=(8640, 512), dtype=int16, chunksize=(8640, 512), chunktype=numpy.ndarray>\n",
-       "Coordinates:\n",
-       "  * time     (time) timedelta64[ns] 00:00:00 00:00:10 ... 23:59:40 23:59:50\n",
-       "Dimensions without coordinates: nheights\n",
-       "Attributes:\n",
-       "    long_name:  MMCR Reflectivity with Clutter Removed\n",
-       "    units:      dBZ (X100)\n",
-       "    comment:    Divide ReflectivityNoClutter by 100 to get dBZ
" - ], - "text/plain": [ - "\n", - "dask.array\n", - "Coordinates:\n", - " * time (time) timedelta64[ns] 00:00:00 00:00:10 ... 23:59:40 23:59:50\n", - "Dimensions without coordinates: nheights\n", - "Attributes:\n", - " long_name: MMCR Reflectivity with Clutter Removed\n", - " units: dBZ (X100)\n", - " comment: Divide ReflectivityNoClutter by 100 to get dBZ" - ] - }, - "execution_count": 14, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "ds.ReflectivityNoClutter.data" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", - "metadata": {}, - "outputs": [ - { - "ename": "UFuncTypeError", - "evalue": "Cannot cast ufunc 'greater_equal' input 0 from dtype(' 5\u001b[0m ts_ax \u001b[38;5;241m=\u001b[39m \u001b[43mts_display\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mplot\u001b[49m\u001b[43m(\u001b[49m\u001b[43mv\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43msubplot_index\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mi\u001b[49m\u001b[43m,\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mset_title\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mds\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mvariables\u001b[49m\u001b[43m[\u001b[49m\u001b[43mv\u001b[49m\u001b[43m]\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mattrs\u001b[49m\u001b[43m[\u001b[49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43mlong_name\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m,\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 6\u001b[0m ts_ax\u001b[38;5;241m.\u001b[39mgrid()\n\u001b[1;32m 8\u001b[0m plt\u001b[38;5;241m.\u001b[39mshow()\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/act/plotting/timeseriesdisplay.py:588\u001b[0m, in \u001b[0;36mTimeSeriesDisplay.plot\u001b[0;34m(self, field, dsname, subplot_index, cmap, set_title, add_nan, day_night_background, invert_y_axis, abs_limits, time_rng, y_rng, use_var_for_y, set_shading, assessment_overplot, overplot_marker, overplot_behind, overplot_markersize, assessment_overplot_category, assessment_overplot_category_color, force_line_plot, labels, cbar_label, cbar_h_adjust, secondary_y, y_axis_flag_meanings, colorbar_labels, cb_friendly, **kwargs)\u001b[0m\n\u001b[1;32m 586\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124medgecolors\u001b[39m\u001b[38;5;124m'\u001b[39m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;129;01min\u001b[39;00m kwargs\u001b[38;5;241m.\u001b[39mkeys():\n\u001b[1;32m 587\u001b[0m kwargs[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124medgecolors\u001b[39m\u001b[38;5;124m'\u001b[39m] \u001b[38;5;241m=\u001b[39m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mface\u001b[39m\u001b[38;5;124m'\u001b[39m\n\u001b[0;32m--> 588\u001b[0m mesh \u001b[38;5;241m=\u001b[39m \u001b[43max\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mpcolormesh\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 589\u001b[0m \u001b[43m \u001b[49m\u001b[43mnp\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43masarray\u001b[49m\u001b[43m(\u001b[49m\u001b[43mxdata\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 590\u001b[0m \u001b[43m \u001b[49m\u001b[43mydata\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 591\u001b[0m \u001b[43m \u001b[49m\u001b[43mdata\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mtranspose\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 592\u001b[0m \u001b[43m \u001b[49m\u001b[43mshading\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mset_shading\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 593\u001b[0m \u001b[43m \u001b[49m\u001b[43mcmap\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcmap\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 594\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 595\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 597\u001b[0m \u001b[38;5;66;03m# Set Title\u001b[39;00m\n\u001b[1;32m 598\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m set_title \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/matplotlib/__init__.py:1442\u001b[0m, in \u001b[0;36m_preprocess_data..inner\u001b[0;34m(ax, data, *args, **kwargs)\u001b[0m\n\u001b[1;32m 1439\u001b[0m \u001b[38;5;129m@functools\u001b[39m\u001b[38;5;241m.\u001b[39mwraps(func)\n\u001b[1;32m 1440\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21minner\u001b[39m(ax, \u001b[38;5;241m*\u001b[39margs, data\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mNone\u001b[39;00m, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs):\n\u001b[1;32m 1441\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m data \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[0;32m-> 1442\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mfunc\u001b[49m\u001b[43m(\u001b[49m\u001b[43max\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;28;43mmap\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43msanitize_sequence\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43margs\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 1444\u001b[0m bound \u001b[38;5;241m=\u001b[39m new_sig\u001b[38;5;241m.\u001b[39mbind(ax, \u001b[38;5;241m*\u001b[39margs, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs)\n\u001b[1;32m 1445\u001b[0m auto_label \u001b[38;5;241m=\u001b[39m (bound\u001b[38;5;241m.\u001b[39marguments\u001b[38;5;241m.\u001b[39mget(label_namer)\n\u001b[1;32m 1446\u001b[0m \u001b[38;5;129;01mor\u001b[39;00m bound\u001b[38;5;241m.\u001b[39mkwargs\u001b[38;5;241m.\u001b[39mget(label_namer))\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/matplotlib/axes/_axes.py:6220\u001b[0m, in \u001b[0;36mAxes.pcolormesh\u001b[0;34m(self, alpha, norm, cmap, vmin, vmax, shading, antialiased, *args, **kwargs)\u001b[0m\n\u001b[1;32m 6217\u001b[0m shading \u001b[38;5;241m=\u001b[39m shading\u001b[38;5;241m.\u001b[39mlower()\n\u001b[1;32m 6218\u001b[0m kwargs\u001b[38;5;241m.\u001b[39msetdefault(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124medgecolors\u001b[39m\u001b[38;5;124m'\u001b[39m, \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mnone\u001b[39m\u001b[38;5;124m'\u001b[39m)\n\u001b[0;32m-> 6220\u001b[0m X, Y, C, shading \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_pcolorargs\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43mpcolormesh\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 6221\u001b[0m \u001b[43m \u001b[49m\u001b[43mshading\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mshading\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mkwargs\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 6222\u001b[0m coords \u001b[38;5;241m=\u001b[39m np\u001b[38;5;241m.\u001b[39mstack([X, Y], axis\u001b[38;5;241m=\u001b[39m\u001b[38;5;241m-\u001b[39m\u001b[38;5;241m1\u001b[39m)\n\u001b[1;32m 6223\u001b[0m \u001b[38;5;66;03m# convert to one dimensional array, except for 3D RGB(A) arrays\u001b[39;00m\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/matplotlib/axes/_axes.py:5786\u001b[0m, in \u001b[0;36mAxes._pcolorargs\u001b[0;34m(self, funcname, shading, *args, **kwargs)\u001b[0m\n\u001b[1;32m 5783\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m X\n\u001b[1;32m 5785\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m ncols \u001b[38;5;241m==\u001b[39m Nx:\n\u001b[0;32m-> 5786\u001b[0m X \u001b[38;5;241m=\u001b[39m \u001b[43m_interp_grid\u001b[49m\u001b[43m(\u001b[49m\u001b[43mX\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 5787\u001b[0m Y \u001b[38;5;241m=\u001b[39m _interp_grid(Y)\n\u001b[1;32m 5788\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m nrows \u001b[38;5;241m==\u001b[39m Ny:\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/matplotlib/axes/_axes.py:5768\u001b[0m, in \u001b[0;36mAxes._pcolorargs.._interp_grid\u001b[0;34m(X)\u001b[0m\n\u001b[1;32m 5766\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m np\u001b[38;5;241m.\u001b[39mshape(X)[\u001b[38;5;241m1\u001b[39m] \u001b[38;5;241m>\u001b[39m \u001b[38;5;241m1\u001b[39m:\n\u001b[1;32m 5767\u001b[0m dX \u001b[38;5;241m=\u001b[39m np\u001b[38;5;241m.\u001b[39mdiff(X, axis\u001b[38;5;241m=\u001b[39m\u001b[38;5;241m1\u001b[39m)\u001b[38;5;241m/\u001b[39m\u001b[38;5;241m2.\u001b[39m\n\u001b[0;32m-> 5768\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m (np\u001b[38;5;241m.\u001b[39mall(\u001b[43mdX\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m>\u001b[39;49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43m \u001b[49m\u001b[38;5;241;43m0\u001b[39;49m) \u001b[38;5;129;01mor\u001b[39;00m np\u001b[38;5;241m.\u001b[39mall(dX \u001b[38;5;241m<\u001b[39m\u001b[38;5;241m=\u001b[39m \u001b[38;5;241m0\u001b[39m)):\n\u001b[1;32m 5769\u001b[0m _api\u001b[38;5;241m.\u001b[39mwarn_external(\n\u001b[1;32m 5770\u001b[0m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mThe input coordinates to \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mfuncname\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m are \u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 5771\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124minterpreted as cell centers, but are not \u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 5774\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124medges, in which case, please supply \u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 5775\u001b[0m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mexplicit cell edges to \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mfuncname\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m.\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[1;32m 5776\u001b[0m X \u001b[38;5;241m=\u001b[39m np\u001b[38;5;241m.\u001b[39mhstack((X[:, [\u001b[38;5;241m0\u001b[39m]] \u001b[38;5;241m-\u001b[39m dX[:, [\u001b[38;5;241m0\u001b[39m]],\n\u001b[1;32m 5777\u001b[0m X[:, :\u001b[38;5;241m-\u001b[39m\u001b[38;5;241m1\u001b[39m] \u001b[38;5;241m+\u001b[39m dX,\n\u001b[1;32m 5778\u001b[0m X[:, [\u001b[38;5;241m-\u001b[39m\u001b[38;5;241m1\u001b[39m]] \u001b[38;5;241m+\u001b[39m dX[:, [\u001b[38;5;241m-\u001b[39m\u001b[38;5;241m1\u001b[39m]]))\n", - "\u001b[0;31mUFuncTypeError\u001b[0m: Cannot cast ufunc 'greater_equal' input 0 from dtype('\n", - "
\n", - " Figure\n", - "
\n", - " \n", - " \n", - " " - ], - "text/plain": [ - "Canvas(toolbar=Toolbar(toolitems=[('Home', 'Reset original view', 'home', 'home'), ('Back', 'Back to previous …" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", - "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", - "\n", - "for i,v in enumerate(variables_to_plot):\n", - " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", - " ts_ax.grid()\n", - "\n", - "plt.show()\n" - ] - }, - { - "cell_type": "markdown", - "id": "194399aa-1907-452b-8ba9-bc31d7f60291", - "metadata": {}, - "source": [ - "## Quality check plots\n", - "#### Define variable for QC plot" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", - "metadata": {}, - "outputs": [], - "source": [ - "qc_variable = 'RadarArtifacts'" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", - "metadata": {}, - "outputs": [], - "source": [ - "# QC Plot\n", - "if ('qc_'+qc_variable) in ds.variables:\n", - "\n", - " # Plot\n", - " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", - " qc_display.add_subplots((2,), figsize = (9.5,10))\n", - " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", - " qc_ax.grid()\n", - " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", - "\n", - " plt.show()\n", - "else:\n", - " print(f'QC not available for the selected field: {qc_variable}')\n" - ] - }, - { - "cell_type": "markdown", - "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", - "metadata": {}, - "source": [ - "## Field selection dropdown menu\n", - "Select variable to be plotted from a dropdown menu" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", - "metadata": {}, - "outputs": [], - "source": [ - "plt.ioff()\n", - "\n", - "# populate dropdown menu with available variables \n", - "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", - "d_variable = 'Reflectivity'\n", - "dropdown = widgets.Dropdown(\n", - " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", - " value= d_variable,\n", - " description='Field:',\n", - " disabled=False,\n", - ")\n", - "dropdown.layout.margin = '0px 30% 0px 20%'\n", - "dropdown.layout.width = '50%'\n", - "\n", - "# set up display\n", - "i_display = act.plotting.TimeSeriesDisplay(ds)\n", - "i_display.add_subplots((1,), figsize = (9.5,5))\n", - "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", - "i_ax.grid()\n", - "i_fig = i_display.fig\n", - "\n", - "# update plot callback function\n", - "def update_plot(change):\n", - " i_ax.cla()\n", - " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", - " i_ax_new.grid()\n", - " i_fig.canvas.draw()\n", - " i_fig.canvas.flush_events()\n", - "\n", - "dropdown.observe(update_plot, names='value')\n", - "\n", - "widgets.AppLayout(\n", - " header=dropdown,\n", - " center=i_fig.canvas,\n", - " pane_heights=[1, 6,1]\n", - ")\n" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.16" - }, - "widgets": { - "application/vnd.jupyter.widget-state+json": { - "state": {}, - "version_major": 2, - "version_minor": 0 - } - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/VAPs/quicklook/ARSCL/.ipynb_checkpoints/arsclbnd1cloth.c1-checkpoint.ipynb b/VAPs/quicklook/ARSCL/.ipynb_checkpoints/arsclbnd1cloth.c1-checkpoint.ipynb deleted file mode 100644 index b365a780..00000000 --- a/VAPs/quicklook/ARSCL/.ipynb_checkpoints/arsclbnd1cloth.c1-checkpoint.ipynb +++ /dev/null @@ -1,1937 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "70840257-70e4-45e2-b491-14bff5a257a3", - "metadata": {}, - "source": [ - "# ARSCLBND1CLOTH.C1 Plots\n", - "\n", - "[Click here](https://www.arm.gov/capabilities/vaps/arscl) for more information about this vap." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "460fd89f-e034-452c-b837-f65c5958264f", - "metadata": {}, - "outputs": [], - "source": [ - "%matplotlib widget\n", - "import ipywidgets as widgets\n", - "\n", - "import matplotlib.pyplot as plt\n", - "import ipywidgets as widgets\n", - "import numpy as np\n", - "import pandas as pd\n", - "import os\n", - "from datetime import datetime\n", - "\n", - "import act\n", - "import xarray as xr\n", - "\n", - "# Data archive directory\n", - "DATA_DIR = r'/data/archive/'\n", - "\n", - "# Datastream info\n", - "DATASTREAM_NAME = 'arsclbnd1cloth'\n", - "DATA_LEVEL = 'c1'\n", - "LOCATIONS = [{'end_date': '2011-03-23', 'facility': 'C1', 'site': 'nsa', 'start_date': '1998-03-25'}, {'end_date': '2011-01-04', 'facility': 'C1', 'site': 'sgp', 'start_date': '1996-11-08'}, {'end_date': '2011-03-07', 'facility': 'C1', 'site': 'twp', 'start_date': '1999-07-01'}, {'end_date': '2009-02-14', 'facility': 'C2', 'site': 'twp', 'start_date': '1998-11-01'}, {'end_date': '2011-02-28', 'facility': 'C3', 'site': 'twp', 'start_date': '2003-01-01'}]" - ] - }, - { - "cell_type": "markdown", - "id": "9faaf875", - "metadata": {}, - "source": [ - "## Define site, facility, and date range" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "ac6764f5", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "The following locations and date ranges are available for this VAP:\n" - ] - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
sitefacilitystart_dateend_date
0nsaC11998-03-252011-03-23
1sgpC11996-11-082011-01-04
2twpC11999-07-012011-03-07
3twpC21998-11-012009-02-14
4twpC32003-01-012011-02-28
\n", - "
" - ], - "text/plain": [ - " site facility start_date end_date\n", - "0 nsa C1 1998-03-25 2011-03-23\n", - "1 sgp C1 1996-11-08 2011-01-04\n", - "2 twp C1 1999-07-01 2011-03-07\n", - "3 twp C2 1998-11-01 2009-02-14\n", - "4 twp C3 2003-01-01 2011-02-28" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "print(\"The following locations and date ranges are available for this VAP:\")\n", - "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" - ] - }, - { - "cell_type": "markdown", - "id": "8d132223", - "metadata": {}, - "source": [ - "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "e563983a", - "metadata": {}, - "outputs": [], - "source": [ - "site_facility = ( 'sgp', 'C1' )\n", - "\n", - "date_start = '2011-01-03'\n", - "date_end = '2011-01-04'" - ] - }, - { - "cell_type": "markdown", - "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", - "metadata": {}, - "source": [ - "## Load data files\n", - "Load data files from /data/archive/" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'/data/archive/sgp/sgparsclbnd1clothC1.c1'" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Compile list of files\n", - "site, facility = site_facility\n", - "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", - "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", - "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", - "dir_path\n" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "6be8f3dc", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "['20110103', '20110104']" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "from datetime import date, timedelta\n", - "import pandas as pd\n", - "\n", - "def get_ARM_formated_dates(start_date, end_date):\n", - " \"\"\"\n", - " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", - " EXAMPLE:\n", - " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", - " >> [\"20180219\", \"20180220\", \"20180221\"] \n", - " \"\"\"\n", - " \n", - " _start_date = pd.to_datetime(start_date)\n", - " _end_date = pd.to_datetime(end_date)\n", - " \n", - " delta = _end_date - _start_date # returns timedelta \n", - " dates = []\n", - "\n", - " for i in range(delta.days + 1):\n", - " day = _start_date + timedelta(days=i)\n", - " day_formated = day.strftime(format=\"%Y%m%d\")\n", - " dates.append(day_formated)\n", - " return dates\n", - "\n", - "\n", - "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "51feea2e", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "['/data/archive/sgp/sgparsclbnd1clothC1.c1/sgparsclbnd1clothC1.c1.20110103.000000.cdf',\n", - " '/data/archive/sgp/sgparsclbnd1clothC1.c1/sgparsclbnd1clothC1.c1.20110104.000000.cdf']" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Filter a list of files based on date pattern\n", - "import glob\n", - "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", - "files_filter = []\n", - "for date in dates:\n", - " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", - " files_filter\n", - "files_filter" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "604409ea", - "metadata": {}, - "outputs": [], - "source": [ - "# this datastream is a bit different. It has trouble merge the individual datasets." - ] - }, - { - "cell_type": "code", - "execution_count": 122, - "id": "ccbe501b", - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "
<xarray.Dataset>\n",
-       "Dimensions:                         (time: 17280, numlayers: 10)\n",
-       "Coordinates:\n",
-       "  * time                            (time) datetime64[ns] 2011-01-01 ... 2011...\n",
-       "Dimensions without coordinates: numlayers\n",
-       "Data variables:\n",
-       "    base_time                       datetime64[ns] 2011-01-01\n",
-       "    time_offset                     (time) timedelta64[ns] 00:00:00 ... NaT\n",
-       "    CloudBaseBestEstimate           (time) float32 -1.0 -1.0 -1.0 ... nan nan\n",
-       "    CloudLayerBottomHeightMplZwang  (time, numlayers) float32 0.0 0.0 ... nan\n",
-       "    CloudLayerTopHeightMplZwang     (time, numlayers) float32 0.0 0.0 ... nan\n",
-       "    qc_CloudLayerTopHeightMplZwang  (time, numlayers) float32 0.0 0.0 ... nan\n",
-       "Attributes:\n",
-       "    Date:                      Wed Jul 13 16:27:17 GMT 2011\n",
-       "    Version:                   $State: Exp $\n",
-       "    Number_Input_Platforms:    3\n",
-       "    Input_Platforms:           sgp30smplcmask1zwangC1.c1,sgpvceil25kC1.b1,sgp...\n",
-       "    Input_Platforms_Versions:  ?????,10.2,1.16\n",
-       "    zeb_platform:              sgparsclbnd1clothC1.c1\n",
-       "    Command_Line:              arsc1/arscl2 -s YYYYMMDD -e YYYYMMDD SITE FACI...\n",
-       "    contact:                    \n",
-       "    comment:                   If all layer top heights are 0, then the first...
" - ], - "text/plain": [ - "\n", - "Dimensions: (time: 17280, numlayers: 10)\n", - "Coordinates:\n", - " * time (time) datetime64[ns] 2011-01-01 ... 2011...\n", - "Dimensions without coordinates: numlayers\n", - "Data variables:\n", - " base_time datetime64[ns] 2011-01-01\n", - " time_offset (time) timedelta64[ns] 00:00:00 ... NaT\n", - " CloudBaseBestEstimate (time) float32 -1.0 -1.0 -1.0 ... nan nan\n", - " CloudLayerBottomHeightMplZwang (time, numlayers) float32 0.0 0.0 ... nan\n", - " CloudLayerTopHeightMplZwang (time, numlayers) float32 0.0 0.0 ... nan\n", - " qc_CloudLayerTopHeightMplZwang (time, numlayers) float32 0.0 0.0 ... nan\n", - "Attributes:\n", - " Date: Wed Jul 13 16:27:17 GMT 2011\n", - " Version: $State: Exp $\n", - " Number_Input_Platforms: 3\n", - " Input_Platforms: sgp30smplcmask1zwangC1.c1,sgpvceil25kC1.b1,sgp...\n", - " Input_Platforms_Versions: ?????,10.2,1.16\n", - " zeb_platform: sgparsclbnd1clothC1.c1\n", - " Command_Line: arsc1/arscl2 -s YYYYMMDD -e YYYYMMDD SITE FACI...\n", - " contact: \n", - " comment: If all layer top heights are 0, then the first..." - ] - }, - "execution_count": 122, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# this datastream require some special treatment to merge the files\n", - "ds_single_1 = xr.load_dataset(\"/data/archive/sgp/sgparsclbnd1clothC1.c1/sgparsclbnd1clothC1.c1.20110101.000000.cdf\")\n", - "ds_single_1\n", - "\n", - "ds_single_2 = xr.load_dataset(\"/data/archive/sgp/sgparsclbnd1clothC1.c1/sgparsclbnd1clothC1.c1.20110102.000000.cdf\")\n", - "ds_single_2\n", - "\n", - "# ds_single_1.time.data = ds_single_1.base_time.data + ds_single_1.time.data\n", - "# ds_single_2.time.data = ds_single_2.base_time.data + ds_single_2.time.data\n", - "\n", - "ds_single_1['time'] = ds_single_1.base_time.data + ds_single_1.time.data * 10000000000\n", - "ds_single_2['time'] = ds_single_2.base_time.data + ds_single_2.time.data * 10000000000\n", - "\n", - "ds_single_1['base_time'] = pd.to_datetime(ds_single_1.base_time.data)\n", - "ds_single_2['base_time'] = pd.to_datetime(ds_single_2.base_time.data)\n", - "\n", - "ds = xr.merge([ds_single_1, ds_single_2], compat='override') \n", - "ds" - ] - }, - { - "cell_type": "code", - "execution_count": 123, - "id": "dde711c1", - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "
<xarray.DataArray 'CloudBaseBestEstimate' (time: 8640)>\n",
-       "array([-1., -1., -1., ..., -1., -1., -1.], dtype=float32)\n",
-       "Coordinates:\n",
-       "  * time     (time) datetime64[ns] 2011-01-02 ... 2011-01-02T23:59:50\n",
-       "Attributes:\n",
-       "    long_name:  LASER Cloud Base Height Best Estimate\n",
-       "    units:      m AGL\n",
-       "    comment:    -3. Data do not exist, -2. Data exist but no retrieval, -1. C...
" - ], - "text/plain": [ - "\n", - "array([-1., -1., -1., ..., -1., -1., -1.], dtype=float32)\n", - "Coordinates:\n", - " * time (time) datetime64[ns] 2011-01-02 ... 2011-01-02T23:59:50\n", - "Attributes:\n", - " long_name: LASER Cloud Base Height Best Estimate\n", - " units: m AGL\n", - " comment: -3. Data do not exist, -2. Data exist but no retrieval, -1. C..." - ] - }, - "execution_count": 123, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "ds_single_2.CloudBaseBestEstimate" - ] - }, - { - "cell_type": "code", - "execution_count": 72, - "id": "7c1839ad", - "metadata": {}, - "outputs": [ - { - "ename": "MergeError", - "evalue": "conflicting values for variable 'base_time' on objects to be combined. You can skip this check by specifying compat='override'.", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/act/io/armfiles.py:143\u001b[0m, in \u001b[0;36mread_netcdf\u001b[0;34m(filenames, concat_dim, return_None, combine, decode_times, use_cftime, use_base_time, combine_attrs, cleanup_qc, keep_variables, **kwargs)\u001b[0m\n\u001b[1;32m 141\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[1;32m 142\u001b[0m \u001b[38;5;66;03m# Read data file with Xarray function\u001b[39;00m\n\u001b[0;32m--> 143\u001b[0m ds \u001b[38;5;241m=\u001b[39m \u001b[43mxr\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mopen_mfdataset\u001b[49m\u001b[43m(\u001b[49m\u001b[43mfilenames\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 145\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m except_tuple \u001b[38;5;28;01mas\u001b[39;00m exception:\n\u001b[1;32m 146\u001b[0m \u001b[38;5;66;03m# If requested return None for File not found error\u001b[39;00m\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/backends/api.py:1026\u001b[0m, in \u001b[0;36mopen_mfdataset\u001b[0;34m(paths, chunks, concat_dim, compat, preprocess, engine, data_vars, coords, combine, parallel, join, attrs_file, combine_attrs, **kwargs)\u001b[0m\n\u001b[1;32m 1023\u001b[0m \u001b[38;5;28;01melif\u001b[39;00m combine \u001b[38;5;241m==\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mby_coords\u001b[39m\u001b[38;5;124m\"\u001b[39m:\n\u001b[1;32m 1024\u001b[0m \u001b[38;5;66;03m# Redo ordering from coordinates, ignoring how they were ordered\u001b[39;00m\n\u001b[1;32m 1025\u001b[0m \u001b[38;5;66;03m# previously\u001b[39;00m\n\u001b[0;32m-> 1026\u001b[0m combined \u001b[38;5;241m=\u001b[39m \u001b[43mcombine_by_coords\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 1027\u001b[0m \u001b[43m \u001b[49m\u001b[43mdatasets\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1028\u001b[0m \u001b[43m \u001b[49m\u001b[43mcompat\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcompat\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1029\u001b[0m \u001b[43m \u001b[49m\u001b[43mdata_vars\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mdata_vars\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1030\u001b[0m \u001b[43m \u001b[49m\u001b[43mcoords\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcoords\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1031\u001b[0m \u001b[43m \u001b[49m\u001b[43mjoin\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mjoin\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1032\u001b[0m \u001b[43m \u001b[49m\u001b[43mcombine_attrs\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcombine_attrs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1033\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 1034\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/core/combine.py:982\u001b[0m, in \u001b[0;36mcombine_by_coords\u001b[0;34m(data_objects, compat, data_vars, coords, fill_value, join, combine_attrs, datasets)\u001b[0m\n\u001b[1;32m 981\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m \u001b[38;5;28mvars\u001b[39m, datasets_with_same_vars \u001b[38;5;129;01min\u001b[39;00m grouped_by_vars:\n\u001b[0;32m--> 982\u001b[0m concatenated \u001b[38;5;241m=\u001b[39m \u001b[43m_combine_single_variable_hypercube\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 983\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43mlist\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mdatasets_with_same_vars\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 984\u001b[0m \u001b[43m \u001b[49m\u001b[43mfill_value\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mfill_value\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 985\u001b[0m \u001b[43m \u001b[49m\u001b[43mdata_vars\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mdata_vars\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 986\u001b[0m \u001b[43m \u001b[49m\u001b[43mcoords\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcoords\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 987\u001b[0m \u001b[43m \u001b[49m\u001b[43mcompat\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcompat\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 988\u001b[0m \u001b[43m \u001b[49m\u001b[43mjoin\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mjoin\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 989\u001b[0m \u001b[43m \u001b[49m\u001b[43mcombine_attrs\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcombine_attrs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 990\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 991\u001b[0m concatenated_grouped_by_data_vars\u001b[38;5;241m.\u001b[39mappend(concatenated)\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/core/combine.py:629\u001b[0m, in \u001b[0;36m_combine_single_variable_hypercube\u001b[0;34m(datasets, fill_value, data_vars, coords, compat, join, combine_attrs)\u001b[0m\n\u001b[1;32m 624\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\n\u001b[1;32m 625\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mAt least one Dataset is required to resolve variable names \u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 626\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mfor combined hypercube.\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 627\u001b[0m )\n\u001b[0;32m--> 629\u001b[0m combined_ids, concat_dims \u001b[38;5;241m=\u001b[39m \u001b[43m_infer_concat_order_from_coords\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mlist\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mdatasets\u001b[49m\u001b[43m)\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 631\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m fill_value \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[1;32m 632\u001b[0m \u001b[38;5;66;03m# check that datasets form complete hypercube\u001b[39;00m\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/core/combine.py:149\u001b[0m, in \u001b[0;36m_infer_concat_order_from_coords\u001b[0;34m(datasets)\u001b[0m\n\u001b[1;32m 148\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mlen\u001b[39m(datasets) \u001b[38;5;241m>\u001b[39m \u001b[38;5;241m1\u001b[39m \u001b[38;5;129;01mand\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m concat_dims:\n\u001b[0;32m--> 149\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\n\u001b[1;32m 150\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mCould not find any dimension coordinates to use to \u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 151\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124morder the datasets for concatenation\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 152\u001b[0m )\n\u001b[1;32m 154\u001b[0m combined_ids \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mdict\u001b[39m(\u001b[38;5;28mzip\u001b[39m(tile_ids, datasets))\n", - "\u001b[0;31mValueError\u001b[0m: Could not find any dimension coordinates to use to order the datasets for concatenation", - "\nDuring handling of the above exception, another exception occurred:\n", - "\u001b[0;31mMergeError\u001b[0m Traceback (most recent call last)", - "Cell \u001b[0;32mIn[72], line 5\u001b[0m\n\u001b[1;32m 1\u001b[0m files_filter \u001b[38;5;241m=\u001b[39m \\\n\u001b[1;32m 2\u001b[0m [\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m/data/archive/sgp/sgparsclbnd1clothC1.c1/sgparsclbnd1clothC1.c1.20110104.000000.cdf\u001b[39m\u001b[38;5;124m'\u001b[39m,\n\u001b[1;32m 3\u001b[0m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124m/data/archive/sgp/sgparsclbnd1clothC1.c1/sgparsclbnd1clothC1.c1.20110106.000000.cdf\u001b[39m\u001b[38;5;124m'\u001b[39m]\n\u001b[0;32m----> 5\u001b[0m ds_multi \u001b[38;5;241m=\u001b[39m \u001b[43mact\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mio\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43marmfiles\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mread_netcdf\u001b[49m\u001b[43m(\u001b[49m\u001b[43mfiles_list\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 6\u001b[0m ds_multi\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/act/io/armfiles.py:164\u001b[0m, in \u001b[0;36mread_netcdf\u001b[0;34m(filenames, concat_dim, return_None, combine, decode_times, use_cftime, use_base_time, combine_attrs, cleanup_qc, keep_variables, **kwargs)\u001b[0m\n\u001b[1;32m 157\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m (\n\u001b[1;32m 158\u001b[0m kwargs[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mcombine\u001b[39m\u001b[38;5;124m'\u001b[39m] \u001b[38;5;241m!=\u001b[39m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mnested\u001b[39m\u001b[38;5;124m'\u001b[39m\n\u001b[1;32m 159\u001b[0m \u001b[38;5;129;01mand\u001b[39;00m \u001b[38;5;28mtype\u001b[39m(exception)\u001b[38;5;241m.\u001b[39m\u001b[38;5;18m__name__\u001b[39m \u001b[38;5;241m==\u001b[39m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mValueError\u001b[39m\u001b[38;5;124m'\u001b[39m\n\u001b[1;32m 160\u001b[0m \u001b[38;5;129;01mand\u001b[39;00m exception\u001b[38;5;241m.\u001b[39margs[\u001b[38;5;241m0\u001b[39m] \u001b[38;5;241m==\u001b[39m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mCould not find any dimension coordinates \u001b[39m\u001b[38;5;124m'\u001b[39m\n\u001b[1;32m 161\u001b[0m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mto use to order the datasets for concatenation\u001b[39m\u001b[38;5;124m'\u001b[39m\n\u001b[1;32m 162\u001b[0m ):\n\u001b[1;32m 163\u001b[0m kwargs[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mcombine\u001b[39m\u001b[38;5;124m'\u001b[39m] \u001b[38;5;241m=\u001b[39m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mnested\u001b[39m\u001b[38;5;124m'\u001b[39m\n\u001b[0;32m--> 164\u001b[0m ds \u001b[38;5;241m=\u001b[39m \u001b[43mxr\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mopen_mfdataset\u001b[49m\u001b[43m(\u001b[49m\u001b[43mfilenames\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 166\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m 167\u001b[0m \u001b[38;5;66;03m# When all else fails raise the orginal exception\u001b[39;00m\n\u001b[1;32m 168\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m exception\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/backends/api.py:1013\u001b[0m, in \u001b[0;36mopen_mfdataset\u001b[0;34m(paths, chunks, concat_dim, compat, preprocess, engine, data_vars, coords, combine, parallel, join, attrs_file, combine_attrs, **kwargs)\u001b[0m\n\u001b[1;32m 1009\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[1;32m 1010\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m combine \u001b[38;5;241m==\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mnested\u001b[39m\u001b[38;5;124m\"\u001b[39m:\n\u001b[1;32m 1011\u001b[0m \u001b[38;5;66;03m# Combined nested list by successive concat and merge operations\u001b[39;00m\n\u001b[1;32m 1012\u001b[0m \u001b[38;5;66;03m# along each dimension, using structure given by \"ids\"\u001b[39;00m\n\u001b[0;32m-> 1013\u001b[0m combined \u001b[38;5;241m=\u001b[39m \u001b[43m_nested_combine\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 1014\u001b[0m \u001b[43m \u001b[49m\u001b[43mdatasets\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1015\u001b[0m \u001b[43m \u001b[49m\u001b[43mconcat_dims\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mconcat_dim\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1016\u001b[0m \u001b[43m \u001b[49m\u001b[43mcompat\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcompat\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1017\u001b[0m \u001b[43m \u001b[49m\u001b[43mdata_vars\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mdata_vars\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1018\u001b[0m \u001b[43m \u001b[49m\u001b[43mcoords\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcoords\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1019\u001b[0m \u001b[43m \u001b[49m\u001b[43mids\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mids\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1020\u001b[0m \u001b[43m \u001b[49m\u001b[43mjoin\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mjoin\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1021\u001b[0m \u001b[43m \u001b[49m\u001b[43mcombine_attrs\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcombine_attrs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1022\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 1023\u001b[0m \u001b[38;5;28;01melif\u001b[39;00m combine \u001b[38;5;241m==\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mby_coords\u001b[39m\u001b[38;5;124m\"\u001b[39m:\n\u001b[1;32m 1024\u001b[0m \u001b[38;5;66;03m# Redo ordering from coordinates, ignoring how they were ordered\u001b[39;00m\n\u001b[1;32m 1025\u001b[0m \u001b[38;5;66;03m# previously\u001b[39;00m\n\u001b[1;32m 1026\u001b[0m combined \u001b[38;5;241m=\u001b[39m combine_by_coords(\n\u001b[1;32m 1027\u001b[0m datasets,\n\u001b[1;32m 1028\u001b[0m compat\u001b[38;5;241m=\u001b[39mcompat,\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 1032\u001b[0m combine_attrs\u001b[38;5;241m=\u001b[39mcombine_attrs,\n\u001b[1;32m 1033\u001b[0m )\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/core/combine.py:365\u001b[0m, in \u001b[0;36m_nested_combine\u001b[0;34m(datasets, concat_dims, compat, data_vars, coords, ids, fill_value, join, combine_attrs)\u001b[0m\n\u001b[1;32m 362\u001b[0m _check_shape_tile_ids(combined_ids)\n\u001b[1;32m 364\u001b[0m \u001b[38;5;66;03m# Apply series of concatenate or merge operations along each dimension\u001b[39;00m\n\u001b[0;32m--> 365\u001b[0m combined \u001b[38;5;241m=\u001b[39m \u001b[43m_combine_nd\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 366\u001b[0m \u001b[43m \u001b[49m\u001b[43mcombined_ids\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 367\u001b[0m \u001b[43m \u001b[49m\u001b[43mconcat_dims\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 368\u001b[0m \u001b[43m \u001b[49m\u001b[43mcompat\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcompat\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 369\u001b[0m \u001b[43m \u001b[49m\u001b[43mdata_vars\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mdata_vars\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 370\u001b[0m \u001b[43m \u001b[49m\u001b[43mcoords\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcoords\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 371\u001b[0m \u001b[43m \u001b[49m\u001b[43mfill_value\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mfill_value\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 372\u001b[0m \u001b[43m \u001b[49m\u001b[43mjoin\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mjoin\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 373\u001b[0m \u001b[43m \u001b[49m\u001b[43mcombine_attrs\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcombine_attrs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 374\u001b[0m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 375\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m combined\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/core/combine.py:239\u001b[0m, in \u001b[0;36m_combine_nd\u001b[0;34m(combined_ids, concat_dims, data_vars, coords, compat, fill_value, join, combine_attrs)\u001b[0m\n\u001b[1;32m 235\u001b[0m \u001b[38;5;66;03m# Each iteration of this loop reduces the length of the tile_ids tuples\u001b[39;00m\n\u001b[1;32m 236\u001b[0m \u001b[38;5;66;03m# by one. It always combines along the first dimension, removing the first\u001b[39;00m\n\u001b[1;32m 237\u001b[0m \u001b[38;5;66;03m# element of the tuple\u001b[39;00m\n\u001b[1;32m 238\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m concat_dim \u001b[38;5;129;01min\u001b[39;00m concat_dims:\n\u001b[0;32m--> 239\u001b[0m combined_ids \u001b[38;5;241m=\u001b[39m \u001b[43m_combine_all_along_first_dim\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 240\u001b[0m \u001b[43m \u001b[49m\u001b[43mcombined_ids\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 241\u001b[0m \u001b[43m \u001b[49m\u001b[43mdim\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mconcat_dim\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 242\u001b[0m \u001b[43m \u001b[49m\u001b[43mdata_vars\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mdata_vars\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 243\u001b[0m \u001b[43m \u001b[49m\u001b[43mcoords\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcoords\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 244\u001b[0m \u001b[43m \u001b[49m\u001b[43mcompat\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcompat\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 245\u001b[0m \u001b[43m \u001b[49m\u001b[43mfill_value\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mfill_value\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 246\u001b[0m \u001b[43m \u001b[49m\u001b[43mjoin\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mjoin\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 247\u001b[0m \u001b[43m \u001b[49m\u001b[43mcombine_attrs\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcombine_attrs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 248\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 249\u001b[0m (combined_ds,) \u001b[38;5;241m=\u001b[39m combined_ids\u001b[38;5;241m.\u001b[39mvalues()\n\u001b[1;32m 250\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m combined_ds\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/core/combine.py:275\u001b[0m, in \u001b[0;36m_combine_all_along_first_dim\u001b[0;34m(combined_ids, dim, data_vars, coords, compat, fill_value, join, combine_attrs)\u001b[0m\n\u001b[1;32m 273\u001b[0m combined_ids \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mdict\u001b[39m(\u001b[38;5;28msorted\u001b[39m(group))\n\u001b[1;32m 274\u001b[0m datasets \u001b[38;5;241m=\u001b[39m combined_ids\u001b[38;5;241m.\u001b[39mvalues()\n\u001b[0;32m--> 275\u001b[0m new_combined_ids[new_id] \u001b[38;5;241m=\u001b[39m \u001b[43m_combine_1d\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 276\u001b[0m \u001b[43m \u001b[49m\u001b[43mdatasets\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mdim\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mcompat\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mdata_vars\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mcoords\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mfill_value\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mjoin\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mcombine_attrs\u001b[49m\n\u001b[1;32m 277\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 278\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m new_combined_ids\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/core/combine.py:320\u001b[0m, in \u001b[0;36m_combine_1d\u001b[0;34m(datasets, concat_dim, compat, data_vars, coords, fill_value, join, combine_attrs)\u001b[0m\n\u001b[1;32m 318\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m\n\u001b[1;32m 319\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[0;32m--> 320\u001b[0m combined \u001b[38;5;241m=\u001b[39m \u001b[43mmerge\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 321\u001b[0m \u001b[43m \u001b[49m\u001b[43mdatasets\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 322\u001b[0m \u001b[43m \u001b[49m\u001b[43mcompat\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcompat\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 323\u001b[0m \u001b[43m \u001b[49m\u001b[43mfill_value\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mfill_value\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 324\u001b[0m \u001b[43m \u001b[49m\u001b[43mjoin\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mjoin\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 325\u001b[0m \u001b[43m \u001b[49m\u001b[43mcombine_attrs\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcombine_attrs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 326\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 328\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m combined\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/core/merge.py:1025\u001b[0m, in \u001b[0;36mmerge\u001b[0;34m(objects, compat, join, fill_value, combine_attrs)\u001b[0m\n\u001b[1;32m 1022\u001b[0m obj \u001b[38;5;241m=\u001b[39m obj\u001b[38;5;241m.\u001b[39mto_dataset(promote_attrs\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mTrue\u001b[39;00m) \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(obj, DataArray) \u001b[38;5;28;01melse\u001b[39;00m obj\n\u001b[1;32m 1023\u001b[0m dict_like_objects\u001b[38;5;241m.\u001b[39mappend(obj)\n\u001b[0;32m-> 1025\u001b[0m merge_result \u001b[38;5;241m=\u001b[39m \u001b[43mmerge_core\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 1026\u001b[0m \u001b[43m \u001b[49m\u001b[43mdict_like_objects\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1027\u001b[0m \u001b[43m \u001b[49m\u001b[43mcompat\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1028\u001b[0m \u001b[43m \u001b[49m\u001b[43mjoin\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1029\u001b[0m \u001b[43m \u001b[49m\u001b[43mcombine_attrs\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcombine_attrs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1030\u001b[0m \u001b[43m \u001b[49m\u001b[43mfill_value\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mfill_value\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1031\u001b[0m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 1032\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m Dataset\u001b[38;5;241m.\u001b[39m_construct_direct(\u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mmerge_result\u001b[38;5;241m.\u001b[39m_asdict())\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/core/merge.py:757\u001b[0m, in \u001b[0;36mmerge_core\u001b[0;34m(objects, compat, join, combine_attrs, priority_arg, explicit_coords, indexes, fill_value)\u001b[0m\n\u001b[1;32m 755\u001b[0m collected \u001b[38;5;241m=\u001b[39m collect_variables_and_indexes(aligned, indexes\u001b[38;5;241m=\u001b[39mindexes)\n\u001b[1;32m 756\u001b[0m prioritized \u001b[38;5;241m=\u001b[39m _get_priority_vars_and_indexes(aligned, priority_arg, compat\u001b[38;5;241m=\u001b[39mcompat)\n\u001b[0;32m--> 757\u001b[0m variables, out_indexes \u001b[38;5;241m=\u001b[39m \u001b[43mmerge_collected\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 758\u001b[0m \u001b[43m \u001b[49m\u001b[43mcollected\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mprioritized\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mcompat\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcompat\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mcombine_attrs\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcombine_attrs\u001b[49m\n\u001b[1;32m 759\u001b[0m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 761\u001b[0m dims \u001b[38;5;241m=\u001b[39m calculate_dimensions(variables)\n\u001b[1;32m 763\u001b[0m coord_names, noncoord_names \u001b[38;5;241m=\u001b[39m determine_coords(coerced)\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/core/merge.py:302\u001b[0m, in \u001b[0;36mmerge_collected\u001b[0;34m(grouped, prioritized, compat, combine_attrs, equals)\u001b[0m\n\u001b[1;32m 300\u001b[0m variables \u001b[38;5;241m=\u001b[39m [variable \u001b[38;5;28;01mfor\u001b[39;00m variable, _ \u001b[38;5;129;01min\u001b[39;00m elements_list]\n\u001b[1;32m 301\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m--> 302\u001b[0m merged_vars[name] \u001b[38;5;241m=\u001b[39m \u001b[43munique_variable\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 303\u001b[0m \u001b[43m \u001b[49m\u001b[43mname\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mvariables\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mcompat\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mequals\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mget\u001b[49m\u001b[43m(\u001b[49m\u001b[43mname\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mNone\u001b[39;49;00m\u001b[43m)\u001b[49m\n\u001b[1;32m 304\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 305\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m MergeError:\n\u001b[1;32m 306\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m compat \u001b[38;5;241m!=\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mminimal\u001b[39m\u001b[38;5;124m\"\u001b[39m:\n\u001b[1;32m 307\u001b[0m \u001b[38;5;66;03m# we need more than \"minimal\" compatibility (for which\u001b[39;00m\n\u001b[1;32m 308\u001b[0m \u001b[38;5;66;03m# we drop conflicting coordinates)\u001b[39;00m\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/core/merge.py:156\u001b[0m, in \u001b[0;36munique_variable\u001b[0;34m(name, variables, compat, equals)\u001b[0m\n\u001b[1;32m 153\u001b[0m \u001b[38;5;28;01mbreak\u001b[39;00m\n\u001b[1;32m 155\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m equals:\n\u001b[0;32m--> 156\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m MergeError(\n\u001b[1;32m 157\u001b[0m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mconflicting values for variable \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mname\u001b[38;5;132;01m!r}\u001b[39;00m\u001b[38;5;124m on objects to be combined. \u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 158\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mYou can skip this check by specifying compat=\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124moverride\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m.\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 159\u001b[0m )\n\u001b[1;32m 161\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m combine_method:\n\u001b[1;32m 162\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m var \u001b[38;5;129;01min\u001b[39;00m variables[\u001b[38;5;241m1\u001b[39m:]:\n", - "\u001b[0;31mMergeError\u001b[0m: conflicting values for variable 'base_time' on objects to be combined. You can skip this check by specifying compat='override'." - ] - } - ], - "source": [ - "files_filter = \\\n", - "['/data/archive/sgp/sgparsclbnd1clothC1.c1/sgparsclbnd1clothC1.c1.20110104.000000.cdf',\n", - " '/data/archive/sgp/sgparsclbnd1clothC1.c1/sgparsclbnd1clothC1.c1.20110106.000000.cdf']\n", - "\n", - "ds_multi = act.io.armfiles.read_netcdf(files_list)\n", - "ds_multi" - ] - }, - { - "cell_type": "code", - "execution_count": 101, - "id": "b0e5d0d7", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "2 files loaded\n" - ] - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "
<xarray.Dataset>\n",
-       "Dimensions:                         (time: 17280, numlayers: 10)\n",
-       "Coordinates:\n",
-       "  * time                            (time) datetime64[ns] 2011-01-03 ... 2011...\n",
-       "Dimensions without coordinates: numlayers\n",
-       "Data variables:\n",
-       "    base_time                       datetime64[ns] 2011-01-03\n",
-       "    time_offset                     (time) timedelta64[ns] 00:00:00 ... NaT\n",
-       "    CloudBaseBestEstimate           (time) float32 -1.0 -1.0 -1.0 ... nan nan\n",
-       "    CloudLayerBottomHeightMplZwang  (time, numlayers) float32 0.0 0.0 ... nan\n",
-       "    CloudLayerTopHeightMplZwang     (time, numlayers) float32 0.0 0.0 ... nan\n",
-       "    qc_CloudLayerTopHeightMplZwang  (time, numlayers) float32 0.0 0.0 ... nan\n",
-       "Attributes:\n",
-       "    Date:                      Wed Jul 13 16:28:57 GMT 2011\n",
-       "    Version:                   $State: Exp $\n",
-       "    Number_Input_Platforms:    3\n",
-       "    Input_Platforms:           sgp30smplcmask1zwangC1.c1,sgpvceil25kC1.b1,sgp...\n",
-       "    Input_Platforms_Versions:  ?????,10.2,1.16\n",
-       "    zeb_platform:              sgparsclbnd1clothC1.c1\n",
-       "    Command_Line:              arsc1/arscl2 -s YYYYMMDD -e YYYYMMDD SITE FACI...\n",
-       "    contact:                    \n",
-       "    comment:                   If all layer top heights are 0, then the first...
" - ], - "text/plain": [ - "\n", - "Dimensions: (time: 17280, numlayers: 10)\n", - "Coordinates:\n", - " * time (time) datetime64[ns] 2011-01-03 ... 2011...\n", - "Dimensions without coordinates: numlayers\n", - "Data variables:\n", - " base_time datetime64[ns] 2011-01-03\n", - " time_offset (time) timedelta64[ns] 00:00:00 ... NaT\n", - " CloudBaseBestEstimate (time) float32 -1.0 -1.0 -1.0 ... nan nan\n", - " CloudLayerBottomHeightMplZwang (time, numlayers) float32 0.0 0.0 ... nan\n", - " CloudLayerTopHeightMplZwang (time, numlayers) float32 0.0 0.0 ... nan\n", - " qc_CloudLayerTopHeightMplZwang (time, numlayers) float32 0.0 0.0 ... nan\n", - "Attributes:\n", - " Date: Wed Jul 13 16:28:57 GMT 2011\n", - " Version: $State: Exp $\n", - " Number_Input_Platforms: 3\n", - " Input_Platforms: sgp30smplcmask1zwangC1.c1,sgpvceil25kC1.b1,sgp...\n", - " Input_Platforms_Versions: ?????,10.2,1.16\n", - " zeb_platform: sgparsclbnd1clothC1.c1\n", - " Command_Line: arsc1/arscl2 -s YYYYMMDD -e YYYYMMDD SITE FACI...\n", - " contact: \n", - " comment: If all layer top heights are 0, then the first..." - ] - }, - "execution_count": 101, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Load files as a single dataset\n", - "files_list = files_filter \n", - "ds = ds_single_2\n", - "ds = data\n", - "# ds = act.io.armfiles.read_netcdf(files_list, compat='override')\n", - "ds.clean.cleanup()\n", - "print(f'{len(files_list)} files loaded')\n", - "ds\n" - ] - }, - { - "cell_type": "markdown", - "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", - "metadata": {}, - "source": [ - "## Plot time series data\n", - "#### Define the list of variables to be plotted:" - ] - }, - { - "cell_type": "code", - "execution_count": 102, - "id": "d09b789e-84f1-4605-846b-a72c110c8048", - "metadata": {}, - "outputs": [], - "source": [ - "variables_to_plot = ['CloudBaseBestEstimate']" - ] - }, - { - "cell_type": "code", - "execution_count": 115, - "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/home/kefeimo/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/act/plotting/plot.py:81: UserWarning: Could not discern datastreamname and dict or tuple were not provided. Using defaultname of act_datastream!\n", - " warnings.warn(\n" - ] - }, - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "14e3ab08f694414e84a6909356fbe15a", - "version_major": 2, - "version_minor": 0 - }, - "image/png": "iVBORw0KGgoAAAANSUhEUgAAA7YAAAGQCAYAAACakPyaAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAA9hAAAPYQGoP6dpAACHf0lEQVR4nO3deVxUVf8H8M8AwyIiCAqIguKKiqZgKmqJu+GS+ZRPWWblY/WzzbTNrMQWNUvzeVzLLC3tsZ7UNglF3BV3yQ13EVxGXFiUdYD7+4NmmGH2YebOXObzfr18xdx77rnnnnvnNt97zj1HJgiCACIiIiIiIiKJcnN0AYiIiIiIiIhqg4EtERERERERSRoDWyIiIiIiIpI0BrZEREREREQkaQxsiYiIiIiISNIY2BIREREREZGkMbAlIiIiIiIiSWNgS0RERERERJLGwJaIiIiIiIgkjYEtERERERERSRoDWyIiIiIiIpI0BrZEREREREQkaQxsiYiIiIiISNIY2BIREREREZGkMbAlIiIiIiIiSWNgS0RERERERJLGwJaIiIiIiIgkjYEtERERERERSRoDWyIiIiIiIpI0BrZEREREREQkaQxsiYiIiIiISNIY2BIREREREZGkMbAlIiIiIiIiSWNgS0RERERERJLGwJaIiIiIiIgkjYEtERERERERSRoDWyIiIiIiIpI0BrZE5NJWrlwJmUyGQ4cOmb1NTEwMZDIZPv/8c4Np9u/fj0ceeQQRERHw8vJCSEgI4uLiMHXqVK108fHxkMlkev+1aNFCnW779u1a69zd3dG4cWOMGDHCorIDwLFjx/Dss88iMjIS3t7eqF+/PmJiYjB37lzcuXNHq2zx8fEW5W0riYmJkMlkJtM988wzOvXSrFkzjBkzBidOnBChpNaJj49HdHS03nW3bt2CTCZDYmKiVXnXZtsWLVpg+PDhJtOdOnUKiYmJyMzMNCtf1fdM81/jxo0RHx+PP/74w6qymqOoqAiJiYnYvn27WekzMzMNfh+tqddZs2bhl19+0Vmu+j6bWy5bsrROiIikwsPRBSAikpL09HQcPXoUALBixQq88cYbOmk2btyIkSNHIj4+HnPnzkWTJk1w/fp1HDp0CGvXrsW8efO00rds2RJr1qzRycfLy0tn2axZs9CvXz8olUocPXoUM2fORN++fZGeno42bdqYLP/y5csxadIktGvXDm+++SY6dOgApVKJQ4cOYdmyZUhLS8OGDRvMrQ6n4OPjg61btwIAysvLcf78eXz88cfo1asXMjIy0LRpUweXUFxpaWlo1qyZXfdx6tQpzJw5E/Hx8VoPYEz59ttvERUVBUEQoFAosGjRIowYMQK//fYbRowYYfNyFhUVYebMmQBg0UOaV155BWPHjtVZbmm9zpo1C48++ihGjRqltTwmJgZpaWno0KGDRfnZgrV1QkTk7BjYEhFZ4OuvvwYADBs2DBs3bsTevXvRq1cvrTRz585FZGQkNm3aBA+P6tvs448/jrlz5+rk6ePjg549e5q1/zZt2qjTPvDAAwgICMD48eOxevVq9Y9VQ9LS0vB///d/GDRoEH755RetwHnQoEGYOnUqkpOTzSqHM3Fzc9Oqvz59+iAiIgIDBgzAxo0b8fzzzzuwdOIz91pyhOjoaHTr1k39eejQoWjYsCH++9//2iWwtVZERIRd67FBgwZOfZ6IiKSIXZGJiMxUUlKCH374AbGxsfjiiy8AAN98841Outu3b6NRo0ZaQa2Km5ttb7uqIOHGjRsm086aNQsymQxfffWV3tZgT09PjBw50mged+7cwaRJk9C0aVN4enqiZcuWmD59OkpLS9VpVN05V65cqbO9vu6cGzduRJcuXeDl5YXIyEijXbzN5e/vDwCQy+XqZTdv3sSkSZPQoUMH1K9fH8HBwejfvz927dqls/3SpUtx3333oX79+vDz80NUVBTeffddrTQKhQIvvPACmjVrBk9PT0RGRmLmzJkoLy+vdfn1MXd/+up49+7diIuLg7e3N5o2bYr3338fX3/9NWQymd7uxMnJyYiJiYGPjw+ioqK0rvOVK1fiscceAwD069dP3U1X3/k2xdvbG56enlrnCQDKysrw8ccfIyoqCl5eXmjcuDGeffZZ3Lx5Uyvd1q1bER8fj6CgIPj4+CAiIgL/+Mc/UFRUhMzMTDRu3BgAMHPmTHU5n3nmGYvLqc/Ro0cxfPhwBAcHw8vLC2FhYRg2bBiuXLkCoOo8FBYWYtWqVep9q1pI9XVFfuaZZ1C/fn2cPn0aQ4YMga+vL5o0aYI5c+YAAPbt24c+ffrA19cXbdu2xapVq7TKY871bU6dnDt3DmPHjlUfV/v27bF48WKb1BkRkT2xxZaIyEzr169Hbm4unnvuObRp0wZ9+vTBjz/+iAULFqB+/frqdHFxcfj666/x6quv4sknn0RMTIzOD/ea9AVDbm5uJgPhS5cuAQDatm1rNF1FRQW2bt2K2NhYhIeHG01rSElJCfr164cLFy5g5syZ6Ny5M3bt2oXZs2cjPT0dGzdutDjP1NRUPPzww4iLi8PatWtRUVGBuXPnmhWoa1LVn6or8ptvvomGDRti2LBh6jSq94dnzJiB0NBQ3Lt3Dxs2bEB8fDxSU1PVQcfatWsxadIkvPLKK/j888/h5uaG8+fP49SpU+q8FAoFunfvDjc3N3zwwQdo1aoV0tLS8PHHHyMzMxPffvutReXWVFFRobOsNvs7duwYBg0apA6G6tWrh2XLlmH16tV60//111+YOnUq3nnnHYSEhODrr7/GhAkT0Lp1azz44IMYNmwYZs2ahXfffReLFy9GTEwMAKBVq1Ymj7eiogLl5eUQBAE3btzAZ599hsLCQq1uv5WVlXj44Yexa9cuvPXWW+jVqxcuX76MGTNmID4+HocOHYKPjw8yMzMxbNgwPPDAA/jmm28QEBCAq1evIjk5GWVlZWjSpAmSk5MxdOhQTJgwAf/6178AQB3YGVNZWan33KgeVhUWFmLQoEGIjIzE4sWLERISAoVCgW3btuHu3bsAqnpI9O/fH/369cP7778PoKql1hilUonRo0fjxRdfxJtvvokffvgB06ZNQ0FBAdatW4e3334bzZo1w8KFC/HMM88gOjoasbGxAMy7vk3VyalTp9CrVy9ERERg3rx5CA0NxaZNm/Dqq6/i1q1bmDFjhsm6IyJyGIGIyIV9++23AgDh4MGDJtP2799f8Pb2FnJzc7W2XbFihVa6W7duCX369BEACAAEuVwu9OrVS5g9e7Zw9+5drbR9+/ZVp6v5b8KECep027ZtEwAIP/74o6BUKoWioiJhz549Qrt27YQOHTqoy2SIQqEQAAiPP/64eRXzd9n69u2r/rxs2TIBgPDTTz9ppfv0008FAMLmzZsFQRCES5cuCQCEb7/9VidPAMKMGTPUn3v06CGEhYUJxcXF6mUFBQVCYGCgYM7/osaPH6+37po0aSLs3r3b6Lbl5eWCUqkUBgwYIDzyyCPq5S+//LIQEBBgdNsXXnhBqF+/vnD58mWt5Z9//rkAQDh58qTR7Y2dd9U/zXqyZH81t33ssccEX19f4ebNm+plFRUVQocOHQQAwqVLl9TLmzdvLnh7e2vtp7i4WAgMDBReeOEF9bL//e9/AgBh27ZtRo9TRfVdqfnPy8tLWLJkiVba//73vwIAYd26dVrLDx48KABQp//5558FAEJ6errB/d68eVOnPoxRXbuG/u3atUsQBEE4dOiQAED45ZdfjObn6+srjB8/Xme56vusWX+qa1nzuJVKpdC4cWMBgHDkyBH18tu3bwvu7u7ClClTDO7b0PVtrE6GDBkiNGvWTMjPz9da/vLLLwve3t7CnTt3jB4vEZEjsSsyEZEZLl26hG3btmH06NEICAgAADz22GPw8/PT6Y4cFBSEXbt24eDBg5gzZw4efvhhnD17FtOmTUOnTp1w69YtrfStWrXCwYMHdf6pWnk0/fOf/4RcLke9evXQu3dvFBQUYOPGjeoy2dPWrVvh6+uLRx99VGu5qhtjamqqRfkVFhbi4MGDGD16NLy9vdXL/fz8LHrf0sfHR11n+/fvx/r169G2bVskJCQgLS1NK+2yZcsQExMDb29veHh4QC6XIzU1FRkZGeo03bt3R15eHp544gn8+uuvOucLAP744w/069cPYWFhKC8vV/976KGHAAA7duwwWW5D533Lli023d+OHTvQv39/NGrUSL3Mzc0NY8aM0Zu+S5cuiIiIUH/29vZG27ZtcfnyZZPHZMp3332nPs4///wT48ePx0svvYRFixap0/zxxx8ICAjAiBEjtI61S5cuCA0NVXff7dKlCzw9PfH8889j1apVuHjxYq3Lp/Laa6/pPTddunQBALRu3RoNGzbE22+/jWXLlmm15teGTCZDQkKC+rOHhwdat26NJk2aoGvXrurlgYGBCA4O1jkn5lzfhpSUlCA1NRWPPPII6tWrp1X3CQkJKCkpwb59+2xynERE9sCuyEREZvjmm28gCAIeffRR5OXlqZePHDkSa9aswenTpxEVFaW1Tbdu3dTvwCqVSrz99tv44osvMHfuXK1BpLy9vbUG1DHm008/Rf/+/VFUVITNmzdj9uzZGDVqFPbv36/3vVmVRo0aoV69euquy9a4ffs2QkNDdabhCQ4OhoeHB27fvm1Rfrm5uaisrERoaKjOOn3LDHFzc9OpvyFDhiA8PBxTpkxRB7fz58/H1KlT8eKLL+Kjjz5Co0aN4O7ujvfff1/rh/+4ceNQXl6O5cuX4x//+AcqKytx//334+OPP8agQYMAVL3T/PvvvxvsYq4vGK7J0HnXt21t9nf79m2EhIToLNe3DKh6MFOTl5cXiouLDe7DXO3bt9cZPOry5ct466238NRTTyEgIAA3btxAXl4ePD099eahOtZWrVphy5YtmDt3Ll566SUUFhaiZcuWePXVV/Haa6/VqpzNmjUz+p309/fHjh078Mknn+Ddd99Fbm4umjRpgokTJ+K9994z+eqBIfXq1dN6yANUvfseGBiok9bT0xMlJSXqz+Ze34bcvn0b5eXlWLhwIRYuXKg3jTnXNRGRozCwJSIyobKyUj0wzujRo/Wm+eabb/SOeKwil8sxY8YMfPHFF7WaX7Vly5bqH9wPPvggfHx88N5772HhwoV6px5ScXd3x4ABA/Dnn3/iypUrVk0HExQUhP3790MQBK3gNicnB+Xl5eoWQdUPc80BpQDoBL4NGzaETCaDQqHQ2Ze+ZZaoV68eWrVqhb/++ku9bPXq1YiPj8fSpUu10qreidT07LPP4tlnn0VhYSF27tyJGTNmYPjw4Th79iyaN2+ORo0aoXPnzvjkk0/07j8sLKxW5a+pNvsLCgrS+85ybevYVjp37oxNmzbh7Nmz6N69Oxo1aoSgoCCDI3T7+fmp/37ggQfwwAMPoKKiAocOHcLChQsxefJkhISE4PHHH7druTt16oS1a9dCEAQcO3YMK1euxIcffggfHx+88847dt23PpZc3/o0bNgQ7u7uGDduHF566SW9aSIjI2tdTiIie2FgS0RkwqZNm3DlyhW89NJLOt1wAeDll1/Gd999h1mzZsHDwwPXr19HkyZNdNKpWk1sGfS89dZbWLlyJebMmYMXXnhB60d/TdOmTUNSUhImTpyIX3/9VadFTKlUIjk52WA34AEDBuCnn37CL7/8gkceeUS9/LvvvlOvB6paAr29vXHs2DGt7X/99Vetz76+vujevTvWr1+Pzz77TB0Q3717F7///ruZNaDfvXv3cP78eQQHB6uXyWQynVbtY8eOIS0tzeCAWr6+vnjooYdQVlaGUaNG4eTJk2jevDmGDx+OpKQktGrVCg0bNqxVWc1Rm/317dsXSUlJuHXrlvrhQ2VlJf73v/9ZXR5VPdqiFTc9PR1A9QBGw4cPVw8k1qNHD7PycHd3R48ePRAVFYU1a9bgyJEjePzxx21aTkNkMhnuu+8+fPHFF1i5ciWOHDmiXmerlm5zy2HO9W2oTurVq4d+/frh6NGj6Ny5s8EWcyIiZ8XAlogIVe+P6pv2JCEhAStWrICHhwfeffddvUHpCy+8gFdffRUbN27Eww8/jCFDhqBZs2YYMWIEoqKiUFlZifT0dMybNw/169fX6SZZXFxs8N01U3NdyuVyzJo1C2PGjMG///1vvPfeewbTxsXFYenSpZg0aRJiY2Pxf//3f+jYsSOUSiWOHj2Kr776CtHR0QYD26effhqLFy/G+PHjkZmZiU6dOmH37t2YNWsWEhISMHDgQABVP7CfeuopfPPNN2jVqhXuu+8+HDhwAD/88INOnh999BGGDh2qnke3oqICn376KXx9fdWjvJpSWVmprr/KykpcvXoV//nPf5Cbm6s17c3w4cPx0UcfYcaMGejbty/OnDmDDz/8EJGRkVoj4E6cOBE+Pj7o3bs3mjRpAoVCgdmzZ8Pf3x/3338/AODDDz9ESkoKevXqhVdffRXt2rVDSUkJMjMzkZSUhGXLllnVKm5IbfY3ffp0/P777xgwYACmT58OHx8fLFu2DIWFhQCsm4IqOjoaAPDVV1/Bz88P3t7eiIyM1NuNWdOJEyfUdX379m2sX78eKSkpeOSRR9StgY8//jjWrFmDhIQEvPbaa+jevTvkcjmuXLmCbdu24eGHH8YjjzyCZcuWYevWrRg2bBgiIiJQUlKift9ddS36+fmhefPm+PXXXzFgwAAEBgaiUaNGaNGihdFyZmVl6f1ONm7cGK1atcIff/yBJUuWYNSoUWjZsiUEQcD69euRl5en7q4OVLXqbt++Hb///juaNGkCPz8/tGvXzrxKtpC517exOvn3v/+NPn364IEHHsD//d//oUWLFrh79y7Onz+P33//HVu3brVL2YmIbMKxY1cRETmWodFaVf8OHjwoeHp6CqNGjTKYR25uruDj4yOMGDFCEARB+PHHH4WxY8cKbdq0EerXry/I5XIhIiJCGDdunHDq1CmtbU2NjqtUKgVBqB5F9X//+5/eMvTo0UNo2LChkJeXZ/KY09PThfHjxwsRERGCp6en4OvrK3Tt2lX44IMPhJycHK2yaY6KLAhVo7G++OKLQpMmTQQPDw+hefPmwrRp04SSkhKtdPn5+cK//vUvISQkRPD19RVGjBghZGZm6h2N9bfffhM6d+4seHp6ChEREcKcOXOEGTNmWD0qcnBwsNC3b19hw4YNWmlLS0uFN954Q2jatKng7e0txMTECL/88oswfvx4oXnz5up0q1atEvr16yeEhIQInp6eQlhYmDBmzBjh2LFjWvndvHlTePXVV4XIyEhBLpcLgYGBQmxsrDB9+nTh3r17Rsvdt29foWPHjnrXGRq11tz96dt2165dQo8ePQQvLy8hNDRUePPNN9WjWWteM82bNxeGDRumt7w1r4UFCxYIkZGRgru7u8FRsFX0fc/8/f2FLl26CPPnz9e5fpRKpfD5558L9913n+Dt7S3Ur19fiIqKEl544QXh3LlzgiAIQlpamvDII48IzZs3F7y8vISgoCChb9++wm+//aaV15YtW4SuXbsKXl5eAgC9oxSrmBoV+cknnxQEQRBOnz4tPPHEE0KrVq0EHx8fwd/fX+jevbuwcuVKrfzS09OF3r17C/Xq1RMAqOvQ0KjIvr6+eute37VS81yZe32bqpNLly4Jzz33nNC0aVNBLpcLjRs3Fnr16iV8/PHHBuuNiMgZyARBEOwXNhMREZEzGjx4MDIzM3H27FlHF4WIiKjW2BWZiIiojpsyZQq6du2K8PBw3LlzB2vWrEFKSgpWrFjh6KIRERHZBANbIiKiOq6iogIffPABFAoFZDIZOnTogO+//x5PPfWUo4tGRERkE+yKTERERERERJJm+VCIRERERERERE6EgS0RERERERFJGgNbIiIiIiIikjQOHlVHVVZW4tq1a/Dz84NMJnN0cYiIiIiI6jRBEHD37l2EhYXBzY3th2JjYFtHXbt2DeHh4Y4uBhERERGRS8nOzkazZs0cXQyXw8C2jvLz8wMAXLp0CYGBgQ4uTd2nVCqxefNmDB48GHK53NHFqfNY3+JifYuL9S0u1re4WN/iYn2Lq6CgAOHh4erf4SQuBrZ1lKr7sZ+fHxo0aODg0tR9SqUS9erVQ4MGDfg/DhGwvsXF+hYX61tcrG9xsb7Fxfp2DL4G6Bjs/E1ERERERESSxsCWiIiIiIiIJI2BLREREREREUkaA1siIiIiIiKSNAa2REREREREJGkMbImIiIiIiEjSGNgSERERERGRpDGwJSIiIiIiIknzcHQBiIhq43p+MVJOKXDrXhkGRAXjvvCGdt1faoYCW0/noH9UMAa0D7XrvoiIiIjIPAxsiUiy/nf4Ct795ZT6839Sz+MfMU0xb0wXu+xv9JI9OJKVBwBYsz8bMREBWD+pt132RURERETmY1dkIhLV8p0XEP3Bn2jz7kZM+PaA1fnklUIrqFVZd+Qq/srOrU0R9UrNUKiDWpUjWXlIzVDYfF9EREREZBkGtkQkmpiPNuOTpNO4V1YJZSWQeuYmWryz0aq8bpbIDK47lGn7wHbCqsN6l8/87aTN90VERERElmFgS0SiWL7zAu4UKvWus6bl9naJYHBdtxa2fc92wOfbDK5T5JfYdF9EREREZDkGtkQkiqTj1w2uS7t42+L8sgr1t9i2bORr8wGkLtwqMrgu1N/bpvsiIiIiIssxsCUiUSR0amJwXVzLIIvz6xCgv8V2+rAoi/Myxc/L8K1yxsiONt8fEREREVmGgS0RiWLig60Q6CvXu27Fs90tzi86EIgJ99daFhMRYJcpeBY83lXv8laNfTnlDxEREZETYGBLRKI58v5gNPL1VH8e0K4xMucMszq/H5/vofXZXlPvDGgfiohAH61loQ28kDo13i77IyIiIiLLMLAlIlEF1fdS/21NS62j/F98a63P8+00Vy4RERERWY6BLRFZLTVDgekbjrnkXK6Gx2QmIiIiIrF5OLoARCQ98zadxpLtF1Dxd3S3Zn82YiIC7NYV2BkJjGyJiIiInAYDWyKySMcPklFYVqGz/EhWHlIzFCYHUxIk2tZZM5A9fPkO/jxxDf2jgjmAFBEREZGDMbAlIrPN23Rab1CrMn/zWZcJ8r7Ycg6Aa7ZWExERETkbvmNLRGZbd/iK0fU3CkpM5iGDzFbFcRqq1moiIiIicgwGtkRktoYaU/XoExXqJ1JJnM/2MzcdXQQiIiIil8WuyERktgAfudH1bw6NEqkkxnVJTEZeSQUCvN2RnjgUqRkKbD2dY9f3YePbNbZLvkRERERkGgNbIjJq3qbT+POEAhduFhod9ukfMU1xX3hD0cplSIt3Nqr/ziup0Ppsr/dhYyICXObdYiIiIiJnxK7If9u5cydGjBiBsLAwyGQy/PLLL1rrBUFAYmIiwsLC4OPjg/j4eJw8eVIrTWlpKV555RU0atQIvr6+GDlyJK5c0X4nMTc3F+PGjYO/vz/8/f0xbtw45OXlaaXJysrCiBEj4Ovri0aNGuHVV19FWVmZPQ6byKiOHyRj4bYLOG8iqO3QpAHmjekiVrEM0gxiDbH1+7Dx7Rpz4CgiIiIiB2Ng+7fCwkLcd999WLRokd71c+fOxfz587Fo0SIcPHgQoaGhGDRoEO7evatOM3nyZGzYsAFr167F7t27ce/ePQwfPhwVFdWjyI4dOxbp6elITk5GcnIy0tPTMW7cOPX6iooKDBs2DIWFhdi9ezfWrl2LdevWYerUqfY7eCI9TI2ArKm+t7Q6f9jyfdhmDX1slpe1/srOxctrDuOxZXvxv0NZji4OERERkeik9WvUjh566CE89NBDetcJgoAFCxZg+vTpGD16NABg1apVCAkJwQ8//IAXXngB+fn5WLFiBb7//nsMHDgQALB69WqEh4djy5YtGDJkCDIyMpCcnIx9+/ahR48eAIDly5cjLi4OZ86cQbt27bB582acOnUK2dnZCAsLAwDMmzcPzzzzDD755BM0aNDAouP6x7I0jOjWGlOHWPbuoxjvJJLzUXU7fig6FF/tuuDo4tiNLd+Hrai0WVZWmfpTOtYduar+fDAzFwu3nsfOt/o7sFRERERE4mKLrRkuXboEhUKBwYMHq5d5eXmhb9++2Lt3LwDg8OHDUCqVWmnCwsIQHR2tTpOWlgZ/f391UAsAPXv2hL+/v1aa6OhodVALAEOGDEFpaSkOHz5scdkv3irGwm0X0PGDZLO3Gb1kDyasOow1+7MxYdVhjF6yx+L91kX/O5SFAfO24cG5qVi+s+4FfZrdjhduu4DScvvsRzDaqdn+Qht4WfWwxlC5Kysddzx/ZedqBbUqWXeK2XJLRERELoUttmZQKKrexwsJCdFaHhISgsuXL6vTeHp6omHDhjppVNsrFAoEBwfr5B8cHKyVpuZ+GjZsCE9PT3UafUpLS1FaWqr+XFBQoLW+sKwCnyWfxOQBbY0e69bTOTiSlae17EhWHjYdv4r+Ubplr8u2ns7BjrO30LdtI3ySdAZZucXqdZ8kncbS7Rewf1o/AIBSqdT6r9QsSD1rdrdjfQRBMPvYBaE6ELS2vmpT324wv6yaNF8p0PRrejZ+++sq+rUNwr8f72pxvrXx/d5Mg+s2nbiOUfc1scl+pH59Sw3rW1ysb3GxvsXF+hYX69mxGNhaQCaTaX0WBEFnWU010+hLb02ammbPno2ZM2caLcvP+y6gbel5o2l+uigD4K6z/Lsth1Fy0bEtbWL64rgbMu/JAMjww8Hsv5dq1/+dojK8sTwJ/ZtWL0tJSRGtjObadwP48aIMlQAC5AJmdtNN8/NR/efdXHdu30FSUpJZae/ddYeqLs3dxpAOH6ZCu+OJgJrnqSY3ZbFV+z1xQ38dlZQDQCWSTuYg6f1k/DtOvL7JZzMNnTcBjcsUBo/z8l3gZK4MfnKgU6CAAK/qdXmlwKZsGbIKgdhGgtNf33UZ61tcrG9xsb7FxfoWR1FRkaOL4NIY2JohNLSq26JCoUCTJtUtIDk5OerW1dDQUJSVlSE3N1er1TYnJwe9evVSp7lx44ZO/jdv3tTKZ//+/Vrrc3NzoVQqdVpyNU2bNg1TpkxRfy4oKEB4eLhWmkd7tkKCiRZb79M52LMmXWf50wNjXabFduvpHGSmpWssMRQoyZAtBCIhoSeUSiVSUlIwaNAgyOXG53oV04D5u7RamvOUwGtpwLmPBmulO+t1Fou3Z1q9n8CgQCQk3G9W2sUX9uJ68T0AQEJCglX7UyqVSJiXAt23KYwHtQDw/mjrruX8g9n46WKGkRRVD0I2FYTYreVWsxdB/6hgeLfMwQt6vq+N6nvhw2fj9ebx9rrjWH/iuvrzz5nArFEd8FhsM/zv8BXM+OWUet2VLGDXLTl2v9HHKa/vuspZ7yd1FetbXKxvcbG+xVWzxySJi4GtGSIjIxEaGoqUlBR07Vr1g7WsrAw7duzAp59+CgCIjY2FXC5HSkoKxowZAwC4fv06Tpw4gblz5wIA4uLikJ+fjwMHDqB79+4AgP379yM/P18d/MbFxeGTTz7B9evX1UH05s2b4eXlhdjYWINl9PLygpeXl8H1vp7ueHNoR5PHOqRTU/jIj6FYWd3qFBMRgCGdmhrZqm55d8MJs9M+1ClM638Ucrncaf7H8b9DWVpBrab4z3dgz7SB6s9vDu2IL3dmotzKxkaZTGb2cWv2PKhNXV0uNB3EhjTwwo2C6i76tbmW3d3Na9H+8+RNLLHDNTB6yR71awI/HLyCmIgAhDXw1pu2Z4tAvXX7V3Yu1qdf11n+7i+n0LFpAN7VCGpV7hQp8f2BKwiFc13froD1LS7Wt7hY3+JifYuDdexYHDzqb/fu3UN6ejrS09MBVA0YlZ6ejqysLMhkMkyePBmzZs3Chg0bcOLECTzzzDOoV68exo4dCwDw9/fHhAkTMHXqVKSmpuLo0aN46qmn0KlTJ/Uoye3bt8fQoUMxceJE7Nu3D/v27cPEiRMxfPhwtGvXDgAwePBgdOjQAePGjcPRo0eRmpqKN954AxMnTrR4RGSVR7s2xckPh5qdvlVwffXfK8bHutwcnXnF5o2aFOgrx8QHW9m5NNZLPmH4neyr+aU6y57tHWnP4ticrxlx5tNxLbQ+i3EtC4BN58kFqkar1vfu+9YzOXrT7zyvfzqjA5l3DO7js+TTBtdtOqnb04SIiIjImTCw/duhQ4fQtWtXdYvslClT0LVrV3zwwQcAgLfeeguTJ0/GpEmT0K1bN1y9ehWbN2+Gn5+fOo8vvvgCo0aNwpgxY9C7d2/Uq1cPv//+u1ZLz5o1a9CpUycMHjwYgwcPRufOnfH999+r17u7u2Pjxo3w9vZG7969MWbMGIwaNQqff/651cf2RM/mVm/rilP9dA0PMJlmekIUjrw/2GQ6RxoabfjcNfXXbd13d5PW7eCJ1qbf+T6fc9dkGnuw5Ty5o5fswcJt+kfhDvL11Lv8wdb6pzPq3iLQ4H4OXc41uG5IR8OvQRARERE5A3ZF/lt8fLzWaK01yWQyJCYmIjEx0WAab29vLFy4EAsXLjSYJjAwEKtXrzZaloiICPzxxx8my0z28fOk3mjxzkajaZy5pVblsW4RWLj1PLLu6HZH1uyGrOJuRlxbT+6GIqWDJ279W3Qg0DzQB5f1HJ/KpVuFNtvfD/sum53WVvPkpmYodFpqNSU+3BETVulOA7boKf2vLdwX3hDBfp7IuVums87QtEUyAM/1jkRSkrH3i22L82gTERGRpaTVREMkki5Nze/2nZqhwIzfTuGE4V6eDnP7nm4AkzlnmN605rTYNvQ1/B63uWw5j+1L8cYfMEQ28rXJfjp+kIyT181r/fX1dLdZMLb1tP6uxiqfbNQfbBrrCj17dCe9y8MD6+ldHhsRYLQMtibVebRTMxSYvuGYzbuhExERkXkY2BLpseLZ7malU/0I/+HgFSw/445/frXf9EYimbfptN65aedt0v8u5XdG5kSVqtbBfqYTmWCoHg0pLKuwWXBjavTmi7f0TytgrCv0gPah8PXUfkE5JiLA4HRiBSXizcmnr4X6SFae0weLUg3GiYiI6hIGtkRW0v0RLsOR7Hyn+RH+ze5LZi9PzVAgr1icAEZmxpQ8zmTd4SsWb/Ppn6dtch0MaB+KGCtaTE11hQ5uUN3yrhog7iED72QP6Wi49dnWrZSGWqjfXnfcJvnbg1SDcSIiorqGgS2RHuZ0lp3520mLlovh5dWH0ebdjej4/p8oNPAubFmF7vLvJdpaa8tuzYbcKdIdQdqUszmFNmu5MzWSs7eH9m08JiLAaFfo0Uv24JJGS6+qO/PUIVHwkWvn5evpjqlDogzmY+tWSkMt1LfulTltoGgoGLflAGJERERkGgNbIivl3CvRu/xKXolDfoRHvrMRf5xQQFkJg0EtAAzpoBv0pGfn27NokpWaoUCJebM/6SVGy119L+1uxcYCYX2tixdvFaH3nFQAwJ+vPaheHubvZXCaMHu1Ug5oH4pGvvrnAHTWQNFQMG6rAcSIiIjIPAxsiaw0sJ3+VrFKAaK/Z/fy6sNmt13qGzG3WGn+O6SuxNTgTeawd0DWLtT894i/T9M/svPVvBKMXZ6mdQ01D6qvNy1g31bKTx/trHe5swaK+rqLm2o1JyIiIttjYEtkpUVPxRp9W1TM9+x2njcvoIiJ8Ne7vE/rRrYsjtMwNoWXOdbsz651GQJ89LdA2oKvpzsyFPe0lhl6oDJ6yR5sP3vLYF57L9xBxrXqlntj3bzt2UqpLyB09kBRs5U8wEdusvs4ERER2R4DW6Ja2Dutv9H1YnWfbGvm6L+xzRvqXW5sFGg3aY31pCXllPUPFh61UYu7vQbleqJbM4Q08MKdQu0pnfQ9UDE1H67Kb39dV/9t7JnAgPahaB2s3aJrr+CzSzN/SQWKPjVGnAaqRtYeOG+7wRHJiYiIqPYY2BLZkVjdJ9s1MS+wfbCt/pa2J79K01kWFVIfmXOGGZwGxhwvrz6MzonJeHn1YavzqI2/rhRofbakBf1odp5NymCva6BdaAOzp/sx1AW5psJS84PwD4Z3UP+tGlnZHho38LZLvmLp+EEyFm67gPM3C7Fw2wV0/CDZ0UUiIiKqkxjYko5a9t6kv/l6uovWfdLUfKfGtHhnI/ZcvKOzPPn1vjrLLIlxVYNZFZRU4I8TCkS+s9HqMtrKhFWH0eOTFLPS2qILcVRIfbtdAwcydc+Zik4wbeaXerDG1D6W3AbseZ1LuMOA3jmQC8sq2HJLRERkBwxsieyksKxCtHdsqwaw0f/+rDH6WmpVRv5nl9Xl0TeYlfD3cke7cbcM7d5LMpkurmVQrfYT7Oep9+GArXRvEWhWOnOvwZiIAMS1qt0xk7Y/T+iv+00nnXPqIiIiIiljYEs6atHztM4wt9XaVDoxpyiZ91hXi7fR11KrcuxagcF1phgazMrcQa7MZW3vgtJyAV0/3IyXVx/G9A3H9AZ/p2/crVXZVv+rZ622N6V7yyCTU+Oo5prdfu620bw+e7QT1k/qrV2fTtJzw0mKYZWHovW3ZA/p6LwDYREREUkVA1siO8q9V2YwcAKqWtOMrTfXjwez0G/edpPpLAkEPWtxd3iwtf73Sg0td4TcIiX+OKHAmv3Zeqdnuny7sFb527tLvwDB6NQ45g4YBQCPdYv4O0/t/Kl2pg6Jgm+NwaR8Pd0xdUiUg0pERERUdzGwJbIjY4GTqjXN0HpzXc8vxtvrjlu1bbca829qeqFvK6vyBPRPhSSD/jl0nYXmaMJt390IZWXt8hMjMDQ2Nc7HGzMszo/v19veyQ+Hqv/u1LSB1mciIiKyHQa2RCLRDJz0taZZO+/tpVvWtyz+bGAkW1u0Kl2aM0z9d7MAL63Pzmr5rouYt+k0ymoZ1DpKi0AfrJ/UG39l5+KSgRGTjdEMxk0FuYZW26oXQl0U29y896KJiIjIch6OLgCRK9l+5iYGtA/F1tM5Rteb8uiSPTiUlQcvdxn6tG5UqzJ9NCoa7/9yQmuZrVuV+rSxftRmMSnyirH6mnlT45iy78JtRIU2sEle5rqWX4J5m05jy6kbou5XZfSSPeoHNmv2ZyMmIqDW0wDxlX8iIiIyB1tsiUSkmobF0PQ85sx52uKdjTj0d/BQWiEg1c4DVLlSYFFWISC3pNwmeaU4ILgsqxCwcNsFZNy4Z9X2mq20lvZKtmUvBK0y1WprIiIichUMbIlElP73D/+q6XkCtNap3o005lEr38O1FVuMmO3MgxJdyy+1WV7Hr+TaLC+x1OYdW2O9EIiIiIjsjYEtkYg056/U7KIZ29y8LptHs/NsXyiOGGQX9yT4oq72O7aWXRe16YVAREREVFsMbIn0MLdV0dKQ0M1Ak2ewn7dZ23t5SL9jsMyGnZttHZIH+thu2IGokPo2y0sstemKPKB9KFo28tVaZk4vBCIiIiJbYGBLJKLMO1UjGKtGjlWpNKN17Hp+MYqUtQvl2DZrXNOG9WyW19zHutgsL30c3dCur0X3nYeqR9JeMT621gNHVe2n1lkQERGRC+CoyES1sPucZe8PerjJtEaOVak048f7+BX7LdoXWe74tQKb5SX1gMya8mtuwpZaIiIiEhNbbIlqIe3ibYvSx7cJ1glqAeCcwnRAdfGm9fPVUrUV42MxrmeE3bt1O/MgWYbUNhi3RzBviwHLiIiIqO5jYEtUC3Etg8xO6+vpDn9fud51mXeK0e2jzUa37xIeYEnRyIAB7UMR364xSsvtG3hKscVWa/AoK3MgIiIicgQGtkS10KeNeSO+Roc1wMkPhxocORYAbhUq0frdjQbX/2zgfUVfT/t+jetii9n/DmbbfR9SDPGsDcYtHUHZVbGeiIiI7IeBLZEIisoqAOifv1ZTeSUw89cTBtef+XiozjJPD/dal89cthjR2Bm66PrX87T7PqQYxGiV2Iry2+OQJViNRERE5AAMbIlEcPFWIf7KzgUAkyPFfpt2GS+vPqx3XYkd5kZ1xbhhbI8Is9Pau0XcWVlyXaiCT1e8loiIiMg5uOYvNiJTzPyFbkmr3KHMXLPT/nFCgch3dLslP1UHRka26Ty2VkZS94U3NDttRYXrvG1qyfUs6PmbratERETkKAxsiUTSrYX5wRRQFSxottymZihw/Gq+Trqy8grz82TkoeajfxwvHS0b+1qVvxSrWitYtWq6H9sfdF18x5uIiIhsj4EtkQhksKyVUCXppEL99/KdF/WmKbOyRdHWUjMUmL7hGFIzFKYTO4F/9WllVrox95vfbVmbc5wXS9R28CgpBvNiYvUQERHZj4ejC0DkCoZ3bqL+25JW00oB6kBx3yX9XZmt7SprS6eu5WPCqqrW5TX7sxETEWDyXWJHmzokCt/syURhmeEW7xXjY5F9p9iq/KUZ5GlO92PiAATdP+1xyNKsRyIiIhIbW2yJRHCnsFTdmmnpD/XnvzusDhr1sf1wUpa7V6odHB7JypNEy+3JD4filX6t4O2h2981JiIAA9qHWp23FOMx61tsbVsOIiIiIkuxxZbIhl6Ob4mjWXk4mHkHbUP88EC7YCzdfgF7LtzBngt3/m7N9LcoT1MNsnIZoLRjYFE12JPlO3j752O2L4wdTB0ShalDopCaocD3aZchkwFP9Wxeq6C2LjAZrOp599Ue73BnXM9HaobC5c8HERERGcfAlsjG1jwfp/67pZ6RjY9k6Q4AVRv1feTILVLaNE9DLBnI53ahOGWylQHtQ20aPEmxFdOiwaO0uiLb7x3bq3klmLDqsCS6txMREZHjsCsykR7m/j439kN+3qbTTtFN2BR7BWBBvvqHHbbtyLnOGz1KcQTq2nZFtseoyCpS6d5ujAQvCSIiIslgYEtkJ4u3XTA7bUPvutd54tNHOzu6CA717Lf2nXPYHkGSZjBuTfb2Dty2n7lp3x0QERGRZDGwJbIhza66lrTWfv1sd2TOGYb6nvadtFOsBiNjAy/J9L2c6cRkVk6kWqQUEKmnK7ot2bpVuLa52TuwjW/X2L47ICIiIsliYEtkJ6F+nmanVcVOJz5MsFNpxNOxiR+OZ+fpfb/Y1QgAXl5teERrZ6MZmBYUlxlPqzk1kLorsv3UdpRqIiIiqtsY2BLZyb7pg8xOq9kmmDlnmMn0vVsFWlEicZy8fhdKwTmmIXIGO8/br/usrVtIP/j1hPrvq3klGL1kj4XlsU9oO6pLkzoxcJQ930EmIiJydQxsiezIy928bqyPLNmr9blLuOEpgXw93dEvKqRW5RKb1Af9qY0HW0uj+2xqhgLncu5pLTM2YJNml3J7B2wtG/vZNX8iIiKSPga2RHbUrXlDs9OquqymZiiQnq07JZCv3A2v9GuFkx8OtVn5zGKDV2Jv35PW1D+2tOipWLvlbctwcuvpHL3LDQ3YdOHmXfXf6w9fMVqe1AwFpm84ZvUDjroymvCVO0WOLgIREVGdxcDWAuXl5XjvvfcQGRkJHx8ftGzZEh9++CEqK6s7XQqCgMTERISFhcHHxwfx8fE4efKkVj6lpaV45ZVX0KhRI/j6+mLkyJG4cuWKVprc3FyMGzcO/v7+8Pf3x7hx45CXlyfGYZINac5pa8qWM1U/+g0FGKNjm2HqkCiblEuTJd1HrY1xg+rrn/qnrlsx3n5BLWDbrr/9o4L1Ltc3YNPUn9Lx8cbT6s/v/XoSD87dqjeyHb1kDyasOow1+7MxYdVhi7s3A8C3ey5avI2z0Dze7WdvWXX8REREZBoDWwt8+umnWLZsGRYtWoSMjAzMnTsXn332GRYuXKhOM3fuXMyfPx+LFi3CwYMHERoaikGDBuHu3erWjcmTJ2PDhg1Yu3Ytdu/ejXv37mH48OGoqKhQpxk7dizS09ORnJyM5ORkpKenY9y4caIeryuzZQvRADNHcg2u7w3AsgBDKjQH/bFlt1Vnb8mzZMonRxvQPhStGvtqLdM3YNNf2blYd+SqzvZZd4qx4Yj2A7rUDAWOZOVpLTM1H62+dXnF5Rgwb7uJI3A+1hw/ERERWYeBrQXS0tLw8MMPY9iwYWjRogUeffRRDB48GIcOHQJQ1XqyYMECTJ8+HaNHj0Z0dDRWrVqFoqIi/PDDDwCA/Px8rFixAvPmzcPAgQPRtWtXrF69GsePH8eWLVsAABkZGUhOTsbXX3+NuLg4xMXFYfny5fjjjz9w5swZhx0/maZvKpsVz3Y3a9us3BIAVQFGTESA1rqaAYa1U9CQ5WpT1fYMYgQ7vNn61tDqHgFN/L30Dth0IPOOwe3TLlWvG71kD2b+dlJvusXbzhvMw1CPhQs3CyUXEBo6lk80WruJiIjINhjYWqBPnz5ITU3F2bNnAQB//fUXdu/ejYSEqilaLl26BIVCgcGDB6u38fLyQt++fbF3b9XgQIcPH4ZSqdRKExYWhujoaHWatLQ0+Pv7o0ePHuo0PXv2hL+/vzqNq6vtO3tiGx5t3jQlfT9NBQCsn9QbK8bHYlzPCKwYH2u7EWGdoIVTavPY1tbqfZcdXQSzlVfoTuFTU/cW5o3IfSQrT/2wRnddPqb+lK7+PPPXE+g0Ixn3f7wZSqXh8bQNve/rCPM2ncbAedsxb5PhILVLeIDe5RdvFaLVtI14lN2SiYiIbMbD0QWQkrfffhv5+fmIioqCu7s7Kioq8Mknn+CJJ54AACgUVUFWSIj2iLUhISG4fPmyOo2npycaNmyok0a1vUKhQHCwbnfU4OBgdZqaSktLUVpaqv5cUFCg/ruivBxKpfmD92i+t2fJdmJ57Mv9SL9SNbjSmv3ZiAn3x4/P9zCxlWWU5caPW1UvNdNVVFborbMv/tkZG08oTMaVl3NL1Ns/2DoID7YO0tqfej8a3dYtUV6hey1UVOoGEobOu7VdfysrK9V52uL6Um1nTT1Yuk9r61pF0Dh2WyovL0dZmW3yVZVvbnKGepmioBSjF+/Gj8/3qL7elUp0CK2Pni0CsC8zr1b7XHfkKp64vxnGfn0AZX8H1HdLK/CTnm7OKn1aBTrFPanLR6koLKu6LhZuu4CFGl3OBUFQlzHUz8tgHhUCcCgrDy3e2YhzHw3WWqdZ32R/rG9xsb7FxfoWF+vZsRjYWuDHH3/E6tWr8cMPP6Bjx45IT0/H5MmTERYWhvHjx6vT1ewmKgiCya6jNdPoS28sn9mzZ2PmzJl61+1N24vrJ/Su0is/3x2qYYKSkpLM31AEJ+4A6VeqywcAR7LzMHd1EqJtOLVrXilg7OuhqpfbJdrpLly4gKQy/d0sF8QBiYfckKs01FFCQKBnhVl1nnFdBsAdAFBWVgZzh3U6ePAQis5rR6enNPJS0SxDZUV1fRcWFZq9L03Z2dlISqp6uHP3ru2ur1OnTqFm2U2xdJ8nFbr1Yz4BbdxybPA90r0W9+zZg0v19K+zVFJSEk7cAS7fMf7dSklJAQB08gL2QTttVXcAy66NN9fsRVmFmxnbCXCHgJKLh5Dk4HGkNl4GCstqHnu1mwUl6vNddR8xnLaKgDbvJ+PfcboPmFT1TeJgfYuL9S0u1rc4ioo4+r0jMbC1wJtvvol33nkHjz/+OACgU6dOuHz5MmbPno3x48cjNLSqu6lCoUCTJk3U2+Xk5KhbcUNDQ1FWVobc3FytVtucnBz06tVLnebGjRs6+79586ZOa7DKtGnTMGXKFPXngoIChIeHAwB6xfVC1xrvbBrz1eU0XCmsGuxK1c3aWez/7RSAKzWWylDk3wIJCR1sth9FQQlmHNlpcL2qXrJzi/Dh0d3q5a1atULCwLYGt9urPIkfDxlqkZJh//vm1feNvZexIbPqfWtPT08UmmhhVrn//m6Ib6s9CNWtfVlYl6ndnVLzvL95cAtQXvWj27eeL26VWH7TDg8PR0JCRwDA4gt7cb34ns5+LKFUKpGSkoIOHToAFy1779zSfd7Zn4WfL1n3TmRMeADeeqr2vQleS9uss6x3795oG+KHqfu31Dr/hIQELFiwC0BxjTVV361Bg9ogJSUFgwYNglwuN/g9tNSVInMfGMhQARkSEsyf6mrr6Rz8sD8LAmTo1MwP20/fwp0iJUbHNMHkAYa/o6ZMeV/3XGiqgAxnvVqr9/FJ+maUGe5djap6k+GtA244kVjVcqu6vlX1TfbF+hYX61tcrG9xafaYJPExsLVAUVER3Ny0W9vc3d3V0/1ERkYiNDQUKSkp6Nq1K4Cq1rQdO3bg008/BQDExsZCLpcjJSUFY8aMAQBcv34dJ06cwNy5cwEAcXFxyM/Px4EDB9C9e9XAQ/v370d+fr46+K3Jy8sLXl76u725e3hYdDPTbBV2tptgUH39xxjo62XTsso9jHc/Ve1L7qG9T3c3d6Pl0Lx+4ts2wvaztwAAzRt6Y8fbA8wun7u7dS2IHu6614K7m24LsmYazXDF2oGU3Nzc1Hna8vqyph4s3ae1df3E/c0w+x/3WbWtOTw8PODhYZtb+PBFe3Hpds2gtkqXiIbqOtt1IRc7z9/Wehe3Noy8TqtXm/c3I3POMJPpRi/ZozUa8c7zt9V/L96eiZV7s62eD9qcjumLt2fizxM52PpGP7OPsbQCiJ+3Ex8+3BGpp27AtwBIkMud7h5cl8lZ36JifYuL9S0O1rFjMbC1wIgRI/DJJ58gIiICHTt2xNGjRzF//nw899xzAKp+sE+ePBmzZs1CmzZt0KZNG8yaNQv16tXD2LFjAQD+/v6YMGECpk6diqCgIAQGBuKNN95Ap06dMHDgQABA+/btMXToUEycOBFffvklAOD555/H8OHD0a5dO8ccvJM4fiVP//Kr+eIW5G+1mW5mUr82WPmcbd8NtoYlh8DRmM3XNrSBzrLUDAW2ns5B/6hgnWl0HOnMjXsG1zVrWDUF0EdH3HArLV2kEhnW4p2NRoNbfVPs1FRYVoF5m05rzQv9v0NZWLbjApQVlejZIghyuZv6PKVmKDB/0xmcUhiup5ou3ipCzIebLfp+Xc0rwYRVh//+5I7DX+3H+pf6WJADERGR62Jga4GFCxfi/fffx6RJk5CTk4OwsDC88MIL+OCDD9Rp3nrrLRQXF2PSpEnIzc1Fjx49sHnzZvj5+anTfPHFF/Dw8MCYMWNQXFyMAQMGYOXKlVotQ2vWrMGrr76qHj155MiRWLRokXgH66wMBFa2jresnkhFpLiP4WUVJxjk2aDT1wswfcMxdXA09IsdOP13ALlmfzZiIgJsN9q1Hc349TjO5lj3brW9tHhnI17p10orMAWqgtPZSeZ1G1cN+PTnCQVy7paioKRcvS7rTtXrAmv2Z8NH7oZiS5uW/3anqDaDiMhwJDsfqRkKp3oIQkRE5KwY2FrAz88PCxYswIIFCwymkclkSExMRGJiosE03t7eWLhwIRYuXGgwTWBgIFavXl2L0tZN4+Kaq7vvanqqZ3MHlMYazhMcOIrtZ1+1L2vP2I+Hqt5BXbM/Gx4AymusV81xa23QUpveApaoCmoBZ7t2F267gG/2ZKq7FLd/708Ul1sWgGqOZmyItUGtrcxNPsPAloiIyAycx5YkZUD7UPj76D6PSToujflsHU1qQWVdUTOoVfl6V+2G+BUruHVWhWUVeHn1YbR4Z6PFQa1UnLlxD9fz9b//TERERNUY2JKkXM8vRn6xbpiw7shV/JWd64ASkaVkTtby50jFZbWcI5cPKvDHibr/UGvx1nOOLgIREZHTY2BLkjJ+xX6D6w5lSiuw5ThM9KRkutCTI+06e9PRRSAiInJ6fMeWJOXizUKD67q1aGhwHTkWg3hdAT4eaBvip7Vs3qbTWLM/C3IPN/yrTyQmPtjK4PbjV+xDXkntWnyp9vy83NHE3xtuMpl6cDBTZLBs4LMgP/3TnBEREVE1Brakw5nf2+sSHoBDeqbyCPSV475wBrbWMHW+bRGUOvM15Sh5xeV4ePFe/COmKeaN6YKOHySjUKNr8idJp/HFljOIa9lI//Z1LKjt2bIh9l2UTq+LNsG+eP7BlnisW4TW8hbvbDS6Xf92jfHNs9115to15qV+ra0tJhERkctgYEuS8vOk3np/OB55f7ADSqP7jmNdb5is68fnCOuOXIU7oBXUqhSVCUg97RrdUCc+0BL7Lh42ndCB6nu64bWBbY22pL/Sr5XR0Za/ebY7AGD9pN5IzVBozFurX0xEAEdFJiIiMgPfsSUdzt5tdPboTuq/g+rJkTlnmM334ewtjM5+jsQiynkSobJ/OnLV7vuoPQEtg7yxYnysXXIf0D4UMREBWsvCG/rYfD9uZpzOAVHBOssiAn1w4sOHjAa1ADB1SBS83PWvs6zuKvHlk10kMdcxERGRM2BgS5KjGcwM7dTEcQUhq9TVkXzd6/jDBncI2DT5QZ3Ww5rBaG2sn9Qb8e0aqz8//2BLm+WtsvzpWDwbZ3zQrjn/6IxfX+qFEZ1C0b1FQ3z2aCfsfKu/2fs488kwhDbQfi9WX8vr1tM5BvP4d1wl+usJsImIiEg/dkUmSZNaiOTollZnb4mWsoe7NkVTf28s330BJUpHl8b23rxP/zyxxrrUeroDls5o1DywnjXFM4squBzQPhQ/HMhCaYX+L4RMBtwX3hALn7S+dXrfuwORmqHA9jM3Ed+usd7uxP2jgrFmf7bO8i+f7IKSi4es3jcREZErYostkYPU8QY+g+ryPLZTh0Thka4RphP+rUt4ABp4Sf82bPgdUNuf63E9I7BifCwy5wzDrEc6qpf/+/EuBrfp164RVoyP1erW6+lhoL8wbFfqAe1D8dGoTgbrR1/365iIALbUEhERWYEttkRENmRJq3wDHzmOzXzI5Ei61pK7AUr9Da0GtQn2xbkcw9NqWcLdTQYYaBU1RGaiAj8aVf2OfVyr6m7L7UL99CVHTEQAvn22h85yL7kb7pZaVDS7ULV4a7bsKpV1sMmfiIjIzqTfVEBE5EQsae0T/u4b/tGo6Frvt2mAN1o10u7Ge27WMKODJdWvMcpRq0a+SJkSX+uyqHi4u+EfMU2tz8BEkCvT+rv60/BOoeqWXXMGXxrXMwLTHorS2K24vQpMtewSERGRaQxsSQffwzRfzboS+wexLZg63Vpdh6V3eKJzs+IaEGzwpbuaVwL/ep5o4G1+R5xOTQO0Pk9LaF/rctQ0b0wXbPi/OPXnsBqDKhllZb20bFzfokDxo1Gd0D0yUP2ZlzkREZH0MLAlyRFjVF1nj+35w7uKM46wbM2zjcpK2xzHkaw8lFVoj9ZkLGedeZjtdGF1iWio/vvVgW2NprWkDJppLS87v0VERER1CQNbF2DpDz4pNTqydZnszdKvgzVfHxvFtQCAsnL7fCnEui1o9hA4rSiw454M15OU7oFERERUhYGtC2DwRySetQezzE576+/Riypt+CV1qu/732WxpIv+H8euqf/WNxWOJs0g2JaxaF0euZuIiKiuYmBL5CCOaBVyqqCnDlq+8wJKLWgxvZZXDMC258VLbv6FVTOAc3RLZWqGAjkWDFVcu67INfNiMEtERCRlDGyJiGzks01nLEpfVlEOwHYttjERAZC7a490bEm4lp6Va5NyWLVzAFtP59h2/xbQGsCLMS4REZHkMLAlEpGr/l52hcawa3nFKLNwztbiv6crtdU7tkey8mp1jf1n6wWMXrLHNoWxQv+o4FpsbbuLzBWuVyIiorrG/HkhiJyEM3enleLvYVtMNWN6H3bfhcPdKCixarsW72xEeIC3zcpRUFJudto7hbrdfo9k5dmsLJYa0D4UMREBDi0DIM3vMRERkatjYEsS59iIyVF7F/N9QK33GEXbq/SENPDGpVtFVm2bnWddUFxbinzH7NeY9ZN6IzVDge1nbqJSEIwOIGXLd2yJiIhI2hjYugD+4LOcGK2YVHvOdJrCAnwgg6MfteiyZB5bUXduxID2oRjQPhTfp2XatDjm4kBSRERE0sN3bIkchj+epcCSGOflfq3sVxAz+Xmb/7wy1N/HjiXRz5JY96udF42u1wxAbTvdDxEREUkNA1sX4EytWkR12dQhUfD1dDed0A483YDMOcMs2ibQ11NnWUxEgNFtag4uZXSwqVpEiKkZCmTnFutdTkRERFQTA1siF+IMzzjs3v3VwU5+OBTNG9puMChzvTk0yuJtNOexjQj0xorxsVg/qbfB9FtP5+gM7HQkK88uwaahqX+2n7mp/lszbq5N9+Ga5WdPZCIiIulhYEuSU7fDIqoLdrw9QO9ydxkQYcMRkFUCfeWY+GBVN+iaMZmxGO341Tz131l3SrB42wWj+9lx9pbe5ZrBpq0Ymvonvl1jm+SfV1Sm/nvCqsN4Z90x9WcZOyMTERFJDgNbkjRn62bNlh79tEdWdo1KWjE+VmdZhQDctWA6HlOCfOWYnhCFI+8Ptmr7e6UVWp+Ntb7KZEDfto30rtt74bZV+zdGNfWPvuWaZVL/bUHeqRkKlFdqLztz455lBSQiIiKnwsCWqBY4erJ5XLGaDHWlNScC0zf+U0xEADLnDEOYf3WL75Yp8eqWWlsx1vraPyoYLRvV01l+4Wah/g0sOO/6Aur1k3rrfUBQWwbPzd/4gIqIiEh6GNgSichWP5jF/OFtq/cYXY2hrrRP9WxuctvmQb7InDMMK8bHYlzPCIPvvppzOix9pmCqq29cqyALczTPhFWH9Q5EpdlCW5Nm678ll6ahc0NERETSxcDWBTAWsZwYLYw8L7UnRkOwtV2n9XWljYkIwNQhUSZHHr5TWKbO46NRnWp0v7XdhVPfS3sE55iIAKOBJGBhUGhhUS0diMraLu76zk27kPp68yUiIiJpMH/CQyIissj6Sb2RmqHA9jM3Ed+usTpoXD+pN578Kg17Lt7Ru11ZeYXe5eawJNiPbuqPiQ9E6pTPGFVQqDk6cs3PtbH9zE2zylFbNc9NoK8XHlmy1+77JSIiIvtgYOsCXPH9RiJnMaB9qN5Abc3zcWjxzka924QH1te73NZkkBksnzH6AnZDx2IpS0Y91u4mb/m+NI/9aFauRr5ssiUiIpIaBrYkPRqROoN2yzjDYFd1fR5bS3z9dAz+9d0RneWvD2pjdZ76pvuxR41bExCbYk5XaLPUsi8xuyITERFJDwNbIhvi72H9GCjoN7BDE51lpoI7zYcTTt+yaEZEvWJ8rEVdobUYOnwneIBDRERE4mJgS+TixIgBNPfh9MGYgzzQOgjP9G4hyvulzsQeLb+1xSuUiIhIeupEYJudnY3MzEwUFRWhcePG6NixI7y8vBxdLHIBjmoX4g/vumfM/RF2CfAc2nZp5wvV4HQ/te6KzG8YERGR1Eg2sL18+TKWLVuG//73v8jOztbqnufp6YkHHngAzz//PP7xj3/AzY2zGpHzkcpPZ80f+VIpc11my6Crzr7vzK7IRERELkeSEd9rr72GTp064dy5c/jwww9x8uRJ5Ofno6ysDAqFAklJSejTpw/ef/99dO7cGQcPHnR0kR2KjQ/Og91wbUyEAEZq3x9XCum05rG14YmS2CknIiIiSLTF1tPTExcuXEDjxrrTQgQHB6N///7o378/ZsyYgaSkJFy+fBn333+/A0pK9iBo/e1KP+OJnGNka6fHUZGJiIhcjiQD288++8zstAkJCXYsiTTwdzCR87MqmNKzjSXZWNKDwKpYz873HoNl4k2PiIjI5UiyK7I5/vrrL7i7uzu6GEROxWGDXbEFzGFY9Zbj4FFERETSU2cDW8A+XfauXr2Kp556CkFBQahXrx66dOmCw4cPa+0zMTERYWFh8PHxQXx8PE6ePKmVR2lpKV555RU0atQIvr6+GDlyJK5cuaKVJjc3F+PGjYO/vz/8/f0xbtw45OXl2fx4yLb4g9g0dh8nWzH4feP3kIiIyOXU6cDW1kFGbm4uevfuDblcjj///BOnTp3CvHnzEBAQoE4zd+5czJ8/H4sWLcLBgwcRGhqKQYMG4e7du+o0kydPxoYNG7B27Vrs3r0b9+7dw/Dhw1FRUaFOM3bsWKSnpyM5ORnJyclIT0/HuHHjbHo8VHu1eXZSq+tTYiPjsmeoacev5tkl37o83Y9BvOCIiIhcjiTfsXWUTz/9FOHh4fj222/Vy1q0aKH+WxAELFiwANOnT8fo0aMBAKtWrUJISAh++OEHvPDCC8jPz8eKFSvw/fffY+DAgQCA1atXIzw8HFu2bMGQIUOQkZGB5ORk7Nu3Dz169AAALF++HHFxcThz5gzatWsn3kET2RhHhq42eske9d9f7riEg5dysX5Sb9H2L/XWc5mBv4mIiMj1SLbFtqCgwOg/zRZSW/ntt9/QrVs3PPbYYwgODkbXrl2xfPly9fpLly5BoVBg8ODB6mVeXl7o27cv9u7dCwA4fPgwlEqlVpqwsDBER0er06SlpcHf318d1AJAz5494e/vr07jyjQbY9gwY39awYMLRg/2OuTUDAWOZOVpLTuSlYfUDIXZeeg7H670HrXBfbrihUpEROTiJNtiGxAQYLQrpyAINu+KfPHiRSxduhRTpkzBu+++iwMHDuDVV1+Fl5cXnn76aSgUVT9IQ0JCtLYLCQnB5cuXAQAKhQKenp5o2LChThrV9gqFAsHBwTr7Dw4OVqepqbS0FKWlperPBQUF6r8rKsqhVCrNPk7Nd5Mt2U4sml22K4VKu5SxzESeqn2Wl5frlM1YeSorK9V/l5dbdl5q7seq7cp1y1dRUamTTjON1vRKVj5JqKysPk+2uL7U9W9FPVi6T3PrWqi07FpMPXVD7/KtGTfwYOsgw/vR+LtcqYTSyBh5psojCIJFZba07gRBd5ua9Wlpnprplcrq75/md7HSxPdQH83tHX3fU+3f0eVwFaxvcbG+xcX6Fhfr2bEkG9hu27ZN9H1WVlaiW7dumDVrFgCga9euOHnyJJYuXYqnn35ana5mQG1OkF0zjb70xvKZPXs2Zs6cqXdd2t69uO5ndPda8vPdoWqnSkpKMn9DkZy8LgNQ9Wv+SvYVJCVl2Xwft0oAY18PVb3cKNZOd+78OSQVnzW43eXLblB1lNi7Zw+uWnBeNJ1UVNdBWVkZzG1XPHzkMJSZ2sHp6WvVealonvfy8urr4e7de2bvS1N2djaSki7/nYftrq/Tp0+jZtlNsXSfx2/o1o8+V65eRVJSttn5+hbg73w161NAvfxMJCVlGtyuuLh6m82bU+BT4zItV9aoX6HmPqrdvn3bQH3ov/ZTUlIMlkvffsrLlRr5V+V5/PhxaNaneeejujya6YvKq9dt3bpV/fe58+eRVGr4e6hP5t3qvOauTkJ0oEWb24XR+iabY32Li/UtLta3OIqKihxdBJcm2cC2b9++ou+zSZMm6NChg9ay9u3bY926dQCA0NBQAFUtrk2aNFGnycnJUbfihoaGoqysDLm5uVqttjk5OejVq5c6zY0buq05N2/e1GkNVpk2bRqmTJmi/lxQUIDw8HAAQFxcL3SNCDD7OL+6nIYrhVVduZ1xHuDb+7KwLvM0AKBZeDMkJETbfB9Zd4rw0dHdBter6uXCzULMSq9+T7JN6zZI6Nfa4HYH/8jA7htVwU+v3r1xXzN/q8qXeyAb/7uUAQDw9PREYbl5TwhjY2IxqIN2b4BruzPx62XtIEDzvE8/shWoqGrN8vOrD0VxocXlDQ8PR0JCRwDA4gt7cb34ns5+LKFUKpGSkoKoqCjg0jmLtrV0n/cOXcHai6dMpmvWtCkSEjqZXw4Ah7/ajyPZ+eplMeEBeOupHoY3AjDn1E7klZUAAAYPHgQ/b7nW+vePbkXx3+crISEBr+/bbLDLfqOgICQk3K+17J9f7QeQrzf9oEGDIJfL9a6bvG+zTj9oDw85EhKGAABeS9sMAOjUqRN+1KhPc86Hatua6fOLlZh2sOohZ//+/ZF4ZCcAoE3r1kgYYPh7qM+whXsBVF2Xy894ICbcHz8+b/xc2Ivq+jZW32Q7rG9xsb7FxfoWl2aPSRKfZAPbmk6ePKnVxc3d3R0dO3a06T569+6NM2fOaC07e/YsmjdvDgCIjIxEaGgoUlJS0LVrVwBVrWk7duzAp59+CgCIjY2FXC5HSkoKxowZAwC4fv06Tpw4gblz5wIA4uLikJ+fjwMHDqB79+4AgP379yM/P18d/Nbk5eUFLy8vvevcPTwsuplptgo7401Qc35iN5mbXcoo9zCep2qfHh7aXyF3d3ej5XFzq36t3cPC81JzP9Zw01M+d3fdV+0102i/Y2td93539+rzZMvry5p6sHSf5u5D5mb5tbj+pT5IzVBg+5mbiG/XGAPah5rej8bfHnK50X2aKo+sxvcnNUOhFWjry8+ye4luGWrWp6V1pplervEmgOZ3Ud91bkxqhgJnc+5pLTuSnY+d52+bdU7sxdL6ptphfYuL9S0u1rc4WMeOJdnAdteuXZgyZQoOHjwIoGpwpaKiIvX7ezKZDJs2bVKPPGwLr7/+Onr16oVZs2ZhzJgxOHDgAL766it89dVX6n1OnjwZs2bNQps2bdCmTRvMmjUL9erVw9ixYwEA/v7+mDBhAqZOnYqgoCAEBgbijTfeQKdOndRlbd++PYYOHYqJEyfiyy+/BAA8//zzGD58OEdErkNq8wa4Ld8eF2MALq0BvyQ+Eq+tDWgf6tDgSdPW0zmOLoJDGDru7WduOs25ISIiIuMkOyrykiVLdOZ13bZtGy5duoSLFy/itddew9KlS226z/vvvx8bNmzAf//7X0RHR+Ojjz7CggUL8OSTT6rTvPXWW5g8eTImTZqEbt264erVq9i8eTP8/Kpfpvziiy8watQojBkzBr1790a9evXw+++/a7VkrFmzBp06dcLgwYMxePBgdO7cGd9//71Nj0eqNAcfcnyI5PgSkOsy5wGHJVdo/yjdQevM5YhxiDU7ENTmm2jouOPbNa5FrkRERCQmybbYHjx4EK+99prWsmbNmqm7BY8bNw7Dhg2z+X6HDx+O4cOHG1wvk8mQmJiIxMREg2m8vb2xcOFCLFy40GCawMBArF69ujZFJXJKnMfWviwJ8Gq2ng9oH4qYiACdaYhEKYwD6TvumIgAttYSERFJiGQD26tXr2oN0LRq1Sr14E1AVWB4+/ZtRxSN6gB2l9Ug0/zTuYJSMbpRu9qUqOsn9Va/9xvcwAvzNlcNzuWM1WCoTBdvWj6PueZxm/u+MxERETkPyQa2fn5+uHTpkrqFdvTo0VrrL126hAYNGjiiaE7H1X6YE1HtqN773Xv+lqOLYrYXvjuk/vuPYwpcy9uD9ZN6W5SHM73vTERERJaR7Du2PXr0wHfffWdw/cqVK9Gjh2OmaiAicpSaz7Ec+lzLzjvXHGH7xDXtKRaOZOUhNUNh3wIQERGR05Bsi+2UKVMwcOBABAUF4c0330RwcNXgHzk5Ofj000+xevVqbN682UQurkGM7ppUha3j+rFebEfz62zt9Evq7Z2yg7HtcFRjIiIi1yHZwLZfv35YuHAhXn/9dcyfPx8NGjSATCZDfn4+PDw8sGDBAvTv39/RxSQ7k3LQ7phgT8IVRmQhjmpMRETkOiQb2ALApEmTMGLECPz88884d65qgJM2bdrg0UcfRXh4uINLR/biTKGZpYG1rWJZWwbFYtSnlB9ASF2tqt6C60wmk+meaBHPe3RYA63uyBzVmIiIyLVIOrAFgPDwcLz++us6y48fP44VK1ZgwYIF4heKiMhBajPdjzHO3p38q6e7IeN6Pkc1JiIiclGSD2w1FRQU4L///S9WrFiBQ4cOoXPnzo4ukiSxdY2omrO9h+pcpXEuHNWYiIjIdUl2VGRNO3bswNNPP40mTZpg0qRJ6N+/P86ePYv09HRHF40kisF9Nc1Aytla7XianJyTXS9ERERUd0k2sL1+/TpmzZqF1q1b4/HHH0ejRo2wY8cOuLm54emnn0br1q0dXUSnYWkw4mzBCxFV0xoVWc96fn2JiIjIFUm2K3JkZCQee+wxLF68GIMGDYKbm2RjdKoFS94RFIMlDwWcrYurPfFhiePIwJZtIiIiqvskGw02b94cu3fvxs6dO3H27FlHF8ep1bVutXXteIgcxdTDFUsevuhNKeJ3dfe5m+LtjIiIiJyOZAPbM2fOYPXq1bh+/Truv/9+xMbG4osvvgDw97QTRHWYta29fCjgeuryKX9y+T7132+tO47RS/Y4sDRERETkSJINbAGgd+/e+Oabb3D9+nW8+OKL+Omnn1BRUYFJkyZh+fLluHmTT/DJvupC0CBGsMuAWjyuUtWpGQr8dSVfa9mRrDykZigcVCIiIiJyJEkHtir169fHxIkTkZaWhpMnTyI2NhbvvfcewsLCHF00IqrB2d6LlrLadk6x+7mwY+eZradz9C7ffoYPNImIiFxRnQhsNbVv3x6ff/45rl69ih9//NHRxZEktq7ZD7vJSxBPmVPqHxWsd3l8u8Yil4SIiIicQZ0LbFU8PDwwevRoRxeD7M1OQThj+2q2DsZtORq0wKcwOlwlDh/QPhQxEQFay2IiAjCgfahjCkREREQOJdnpfsh8dW0eW2cOZSwaRdbJ65nqhtpM91Pra1TPjjOuFdQy02rrJ/VGaoYC28/cRHy7xgxqiYiIXBgDWyKyOwbxzqnW0/mYSXO04u/2Xa5FTroGtA9lQEtERER1tysyVWNvzbqHgSIB5gWmjv76p2YocCQrz8GlICIiorqOgS1RLUjtoYG+4ooxSrHU6smZSaouZYZHLyYiIiKyJcl3RS4pKcHChQuxbds25OTkoLKyUmv9kSNHHFQyIiLxWRL3ivFQo39UMNbsz7b7foiIiMi1ST6wfe6555CSkoJHH30U3bt353QqLkZKjVdURWrz2DrbHUVqtzjV6MXsjkxERET2JPnAduPGjUhKSkLv3r0dXZQ6w9m7OnKKF3FJLZBydbY8XbXO6++vas3RiyesOlzbnImIiIi0SD6wbdq0Kfz8/BxdDKpjrA2e63oQaIseETadx9ZmOZG9cfRiIiIisifJDx41b948vP3227h82bZTSNQldW0eW5IeXlP2YU69suqJiIjIFUi+xbZbt24oKSlBy5YtUa9ePcjlcq31d+7ccVDJiIhsz5Y98W3Zek5ERETkSJIPbJ944glcvXoVs2bNQkhICAeP0oOvpNY9vMrJXA79+vNCJSIiIpFIPrDdu3cv0tLScN999zm6KOQAjh5ISmoj/OqrLjGqkA9XxMOqJiIiIlck+Xdso6KiUFxc7OhiEBGJwpadUkw9mKl1DxhG2URERCQSyQe2c+bMwdSpU7F9+3bcvn0bBQUFWv+InBV7zUuD1F5vkFZpiYiIiGxD8l2Rhw4dCgAYMGCA1nJBECCTyVBRUeGIYpGLqotBha2PSWrdt52No7p1Syy+JyIiIhcj+cB227Ztji6C0+MPUssx9NLP2kvJXtcg3901TQZez0RERFT3ST6w7du3r6OLQEQW4BQzLoSnmoiIiEQiyXdss7KyLEp/9epVO5WEHE1qLVG2arlkKzyZy9h3hA8ZiIiIqK6QZGB7//33Y+LEiThw4IDBNPn5+Vi+fDmio6Oxfv16EUvnfNhd035Yt+ZhPYnHllVd6wcoPO9EREQkEkl2Rc7IyMCsWbMwdOhQyOVydOvWDWFhYfD29kZubi5OnTqFkydPolu3bvjss8/w0EMPObrIZEMMkqznbAM3qQZ5I+vUfjYe57oeiIiIiKwlyRbbwMBAfP7557h27RqWLl2Ktm3b4tatWzh37hwA4Mknn8Thw4exZ88eBrXktNgNVBqkdpakVl4iIiIiW5Bki62Kt7c3Ro8ejdGjRzu6KEQApDfnKRERERFRXSDJFlsie2N352q2DtbZ/VVcDn3Uwuc8REREJBIGti6gLjciMgAVl7XXUl2+BomIiIjI8RjYWmn27NmQyWSYPHmyepkgCEhMTERYWBh8fHwQHx+PkydPam1XWlqKV155BY0aNYKvry9GjhyJK1euaKXJzc3FuHHj4O/vD39/f4wbNw55eXkiHBVJhZTfz5Vy2Z2NOXVZm2c/tT5TfPBEREREImFga4WDBw/iq6++QufOnbWWz507F/Pnz8eiRYtw8OBBhIaGYtCgQbh79646zeTJk7FhwwasXbsWu3fvxr179zB8+HBUVFSo04wdOxbp6elITk5GcnIy0tPTMW7cONGOz9mxKyuRYZZ8O/iQgYiIiOoKBrYWunfvHp588kksX74cDRs2VC8XBAELFizA9OnTMXr0aERHR2PVqlUoKirCDz/8AKBqbt0VK1Zg3rx5GDhwILp27YrVq1fj+PHj2LJlC4CqqYySk5Px9ddfIy4uDnFxcVi+fDn++OMPnDlzxqoys7uu/dSFuhVEOIi6UE/Ogg92iIiIiHQxsLXQSy+9hGHDhmHgwIFayy9dugSFQoHBgwerl3l5eaFv377Yu3cvAODw4cNQKpVaacLCwhAdHa1Ok5aWBn9/f/To0UOdpmfPnvD391enIenSbCFzxHunDDDrPksuK0uCZLbtEhERkTOT9HQ/Ylu7di2OHDmCgwcP6qxTKBQAgJCQEK3lISEhuHz5sjqNp6enVkuvKo1qe4VCgeDgYJ38g4OD1Wn0KS0tRWlpqfpzQUGB+u+K8nIolUpTh6em2YJnyXZiqaioVP9dWVlplzKWl5cbXa/aZ810lRUVRstTWVnd5bzcwvOiVT6NruuWqNBTPs36VNFMo3k9WNu6q3meNPMoK1PCzc3ykEmVV4UV9WBpnWueM2MEO12LxiiVSsgEw88nTZVHEASjaWpe3xYfn8z0Npbm6Yz3JFtTHaMrHKszYH2Li/UtLta3uFjPjsXA1kzZ2dl47bXXsHnzZnh7extMV3NqFEEQTE6XUjONvvSm8pk9ezZmzpypd93etL24fsJoEbTk57tD1T6TlJRk/oYiOX1NBsAdAHDt2jUkJV0xvoEVbhQDxr4eqnq5Wqid7szZM0i6e9rgdpcy3aDqKLFr1y5c9LWufMdzquugrKwM5ranHT16FLJs7eD07JXqvFQ0z3tZWfX1UPXAxPJANDs7G0lJVQ947t7VuL7+/BNWxLVqVd3z3U2m02TpNf3XTd360efK1atISsq2KG9rlJRU119ycjI8asS1SmWN769Q/bmm27dvG62PiwWA5vWdkpJiMG2lnv2UK5UG8q/O07zzYWn6usFYfZPtsb7FxfoWF+tbHEVFRY4ugktjYGumw4cPIycnB7GxseplFRUV2LlzJxYtWqR+/1WhUKBJkybqNDk5OepW3NDQUJSVlSE3N1er1TYnJwe9evVSp7lx44bO/m/evKnTGqxp2rRpmDJlivpzQUEBwsPDAQC94nqha0SA2cf61eU0XCmsGvAqISHB7O3Ecm13Jn69fBYA0CQsDAkJnU1sYbnzOfeAdMNdv1X1knH9LuYeS1Mvb9e2HRIeaGlwu6NJp7HjehYA4IEHHkBUqJ9V5Ss8fBX/vVA14ranpycKy817Qti1a1ckdArVWpa5/SI2Zp/XWqZ53mce26bOv0GDBkDhXVgqIiIcCQkdAQCLL+zF9eJ7Vft56CGrW2xTUlLQtm1b4PIFi7a19JouS7+G1edNPxlq1rQpEhI6WZS3NWad3IH8sqreGQ89NBRyd+3I9v2jW1FcUdXSmpCQgNf3bTbYBT0oKAgJCfcb3NeRrDz8++QB9edBgwZBLpfrTTt1fwoqa+zI3UOOhIQhOmlfS9us/tuc82FpeqlTXd/G6ptsh/UtLta3uFjf4tLsMUniY2BrpgEDBuD48eNay5599llERUXh7bffRsuWLREaGoqUlBR07doVQFVL2o4dO/Dpp58CAGJjYyGXy5GSkoIxY8YAAK5fv44TJ05g7ty5AIC4uDjk5+fjwIED6N69OwBg//79yM/PVwe/+nh5ecHLy0vvOg+5h0U3M82WYWe8Cbq5uWn9bY8yyuXGvxqqfXp4aKdzc3c3Wh43t+qWPw8Py86LJg8Py1opVdz1lM/dXbcrq2YazfeCrQlCq7bTOE8aWcjlcqvzBKqOx1KW1rm5+5DZ6Vo0Ri6X6wS2Ndcb6zwuk8mMlrnmdSaXyy27l8B0fVtaZ854T7IXS+ubaof1LS7Wt7hY3+JgHTsWA1sz+fn5ITo6WmuZr68vgoKC1MsnT56MWbNmoU2bNmjTpg1mzZqFevXqYezYsQAAf39/TJgwAVOnTkVQUBACAwPxxhtvoFOnTurBqNq3b4+hQ4di4sSJ+PLLLwEAzz//PIYPH4527dqJeMREJEWc7oeIiIhcEQNbG3rrrbdQXFyMSZMmITc3Fz169MDmzZvh51fd3fSLL76Ah4cHxowZg+LiYgwYMAArV67UahVas2YNXn31VfXoySNHjsSiRYtEPx6yL0eMiqyP2CMlM5iSJp41IiIicmYMbGth+/btWp9lMhkSExORmJhocBtvb28sXLgQCxcuNJgmMDAQq1evtlEpOcWLPXFOUfPwGhSPvab7sQqjYSIiIhIJ57ElSbN2+hl7cZZWWEOcq7akwdnOqdZcyA4sBxEREZEzYWBLJCJnC5IcjYG2s6vlBcsTTERERCJhYEs6nKwRlIg0WNp9mM9SiIiIyBUwsCXJESPuZnBfTbOV2dqBn+zVUs3TZBrriIiIiFwBA1sXYGlQwe6yzk/Kp4gDbtkXa5eIiIhcEQNbIgfhtDfkaLwGiYiIqK5gYEuS5ujWqbrQZVnsrt0MpmxHpqd7hVPVrlMVhoiIiOoyBrZENsTf8SQlprqF1/q1hDrw4IeIiIikgYGtC6gLrYpkG842768UsIWZiIiIyPkxsCXJYWxGRERERESaGNiSDgaOZGuGurSyBdn+bNXezNHSiYiIyJkxsCXSw17hlu2CDDGjjOp9OVtw4+pxsTmnw8WriIiIiFwEA1sXUKfnsZXwr3ZJ1bMNcR5bF+Ki1zgRERGJj4EtkYtz9VZPqbHl+TI1MFat41JeW0RERCQSBrZENiRuF2Hp4Dy2RERERGRPDGxJctiVlVyZLZ+d8LtEREREdQUDWxfArqZE1mMjPBEREZHzY2BLRHbH4NA+zKlXVj0RERG5Aga2pENKLbxS7kpZm4CDwQqZy6HfEF6oREREJBIGtkR6SCm4tzfNVkFbxym1rWYpP9iwlqSuTSmVlYiIiCSNga0LqNPz2BKR3XCUbyIiIpIKBrYkOc7UYlWzLKbCAGeME8Ro9dSsJ1dsZXVWnHqJiIiI6goGtkREElXbFlU+ZCAiIqK6goEtEYmKrYS146hWf541IiIicmYMbF2AM3XdtbW6fGz2wPqSPkvPoUMDUkbDREREJBIGtkQO4ozv29qLKx2rs3Hosww+SCEiIiKRMLAlHWzVc/53DxkoVuG1qsuWVcLLjIiIiKSCgS0RGaUV3Ng4omZgSkRERES2wMDWBXAeW/FIse4YXNYtllyCHMiLiIiI6goGtkS14Oxdlp0F57ElIiIiIntiYEuSxtZGIuvZ/SEDG4SJiIhIJAxsiUQkk2JfZRuTWvdXnrNa4IMnIiIiEgkDWxfAVk1nJX7A5KhuwIwNpYnnjYiIiKSCgS2RBDHgIEP4HIuIiIhcEQNb0uHsLbyCCAV09jqgKjxPRERERAQwsCWyqbrYkKrZOlwXj6+u4TkiIiIiV8TA1gXU5XlsHT11TF1oMXTkITj6/EmRLWvMkoG8pHRfICIiItfDwJaI7K4uPACoi/hggYiIiOoKBrZEDuKqLWBSC6ac7TSJWR6pTc1ERERErouBLRHZnasG8c6AVU9ERESugIGtC6hr3UClfDy2CjKsbUlzhrpjK6B91TzFTnDKiYiIiOyOga0FZs+ejfvvvx9+fn4IDg7GqFGjcObMGa00giAgMTERYWFh8PHxQXx8PE6ePKmVprS0FK+88goaNWoEX19fjBw5EleuXNFKk5ubi3HjxsHf3x/+/v4YN24c8vLy7H2IROTkGKgSERER6WJga4EdO3bgpZdewr59+5CSkoLy8nIMHjwYhYWF6jRz587F/PnzsWjRIhw8eBChoaEYNGgQ7t69q04zefJkbNiwAWvXrsXu3btx7949DB8+HBUVFeo0Y8eORXp6OpKTk5GcnIz09HSMGzdO1OOVAnu1QFqdL/vckoPZ6wrklU1ERETOzMPRBZCS5ORkrc/ffvstgoODcfjwYTz44IMQBAELFizA9OnTMXr0aADAqlWrEBISgh9++AEvvPAC8vPzsWLFCnz//fcYOHAgAGD16tUIDw/Hli1bMGTIEGRkZCA5ORn79u1Djx49AADLly9HXFwczpw5g3bt2llUbsZaVBuaXYd5LRERERGRM2KLbS3k5+cDAAIDAwEAly5dgkKhwODBg9VpvLy80LdvX+zduxcAcPjwYSiVSq00YWFhiI6OVqdJS0uDv7+/OqgFgJ49e8Lf31+dhpxDnegWKsKLt87wbi/p4vvOREREVFewxdZKgiBgypQp6NOnD6KjowEACoUCABASEqKVNiQkBJcvX1an8fT0RMOGDXXSqLZXKBQIDg7W2WdwcLA6TU2lpaUoLS1Vfy4oKFD/XV5eDqVSadGxqViynVgqKivVfwuCYJcylpeXG12v2mfNdJUVFUbLo1n2CgvPi1b5NLqtW6JCT/k0y6SimUZzeh7Bygi1srJSnafW9VWmhJugu39TVHlVWFEPlta5ufsQNI7Rriz4fppaXykYL3PN69vS4xPM2MbSPJ3xnmRrqmN0hWN1BqxvcbG+xcX6Fhfr2bEY2Frp5ZdfxrFjx7B7926ddbIa/TUFQdBZVlPNNPrSG8tn9uzZmDlzpt51aXv34rqf0d1ryc93h+qNuqSkJPM3FMnZKzIA7gCqHgLYo4xXCwFjXw/VPi/f1U6XkZGBpNxTBre7eNkNqo4SO3bsQIaPdeX762Z1HZSVlcHcNyD/+usveF5L11p2Pqu6TCqadVpSUn095OXmmb0vTdnZ2UhKqnq4U3C3Or/kTZsgr0W/kXPnz0FVD+ay9HpJv1Vd18ZcuXoVSUnZFuVtjdJS499PpbLGeqH6c023b982Wh/Z9wDN6zslJcVg2ko9+ykvVxrIvzpP886HpenrBmP1TbbH+hYX61tcrG9xFBUVOboILo2BrRVeeeUV/Pbbb9i5cyeaNWumXh4aGgqgKthq0qSJenlOTo66FTc0NBRlZWXIzc3VarXNyclBr1691Glu3Lihs9+bN2/qtAarTJs2DVOmTFF/LigoQHh4OAAgrlcvdA0PMPv4vrqchiuFVYNdJSQkmL2dWC5uuwBkXwBQVVcJCV1svo+M63cx91iawfWqevnrSj7mn9ivXt6+fXskxLUwuN3xTWex9VomAKBv376IbORrVfnK/7qO788fBwB4enqisNy8J4T33XcfErqEaS07m3oem65e1Fqmed5nndyB/LKq3gABDQOAe/kWlzciIhwJCR0BAIsu7IGiuGrAtaFDhsBLbllgClQ9EU1JSUGb1m2ArIumN9Bg8TV9XIGV546ZTNasaVMkJHSyLG8rfHx8O6AsA6D/WN47uhWoKFevn7xvs8E+80FBQUhIuN/gvk5cLcDnx/epPw8aNAhyuVxv2qn7U1BZo0Xfw0OOhIQhOmlfS9us/tuc82FpeqlTXd/G6ptsh/UtLta3uFjf4tLsMUniY2BrAUEQ8Morr2DDhg3Yvn07IiMjtdZHRkYiNDQUKSkp6Nq1K4Cq1rQdO3bg008/BQDExsZCLpcjJSUFY8aMAQBcv34dJ06cwNy5cwEAcXFxyM/Px4EDB9C9e3cAwP79+5Gfn68Ofmvy8vKCl5eX3nXu7h4W3cw0W4Wd8Sbo7q4RCMlkdimjh4fxr4ZqnzXTubm7Gy2Pu1t186SHh2XnRbt8lgeDAODmpls+zTKpaKbRHjzKuncy3dzc1Hm6yTTqQC6H3IrAVkXrWjCTpXXubmZdyzSO0a4s+H6aWi8z8f2peX3L5XLL7iU2KGNt00uZpfVNtcP6FhfrW1ysb3Gwjh2Lga0FXnrpJfzwww/49ddf4efnp37f1d/fHz4+PpDJZJg8eTJmzZqFNm3aoE2bNpg1axbq1auHsWPHqtNOmDABU6dORVBQEAIDA/HGG2+gU6dO6lGS27dvj6FDh2LixIn48ssvAQDPP/88hg8fbvGIyNbgQD/W41A85Gi8BomIiMgVMbC1wNKlSwEA8fHxWsu//fZbPPPMMwCAt956C8XFxZg0aRJyc3PRo0cPbN68GX5+1S+5fvHFF/Dw8MCYMWNQXFyMAQMGYOXKlVqtT2vWrMGrr76qHj155MiRWLRokX0PkNQEe413zKjDpqwdzIqIiIiI6hYGthYw50e0TCZDYmIiEhMTDabx9vbGwoULsXDhQoNpAgMDsXr1amuKqadM9k3vyupCYGXqCDSvB14aREREROSMOI8tkYNY+76qFNWB+J+IiIiInBgDW5IcBkkkJhnbqYmIiIicHgNbkjQGudKg2Thtt/eXCYDpruXWYnhPREREzoyBLREREREREUkaA1sXwFZN8Tj7a7POcCmwa699sXaJiIjIFTGwJR0MhFkHUsHTREREREQAA1sih2HLGlmDD12IiIiIdDGwdQF1bR5bZxp8yHlKYj1TgZLm5WDtFEUMxoiIiIjInhjYksRJK2Li+6VERERERLbHwJaIyAhn68HgbOUhIiIicgYMbIkkyNouwY5iqLjsokxEREREtsDAlsiG2NXYNGd6R5qIiIiI6gYGti6ArWKkIvBikDyeQiIiIiJdDGxJh7P/cHb28plLYr2JbcaWrdp15VogIiIiotphYEuS5ujAxtH7twV2DSYiIiIiqWNgSzpctSWR9NMcqMraS6MuPABwdbwvEBERkTNjYEtEZISzxXMMMImIiIh0MbAlEpGtghKpxTYMxmyHrd9EREREuhjYEtkQAzjL8P1eIiIiIrIFBrYkOQyFpI3BLBERERHZGgNbl1B3AwkpH5ktp70xl5Tri4iIiIjIEAa2pIPv8LEO7Mmm89jaLCciIiIikjIGtkS1Iv3QikE8EREREUkdA1vSwQGQyBBeG0RERETkjBjYEhEZwWCeiIiIyPkxsCWyIbFiIAZbRERERETVGNiS9Gi8FCpI+AVRBqdERERERLbBwJZIRIxltUn4uQQREREROREGti6AwQOpOcG1IDhDIchifChDREREzoyBLelgIGx+8FUX6krKhyDlrujWc8VjJiIiIjKOgS0R2Z1m/Clj2x8RERER2RgDW9LBQY1Ik+b14JpBqbMds7OVh4iIiMjxGNiS5AgG/nYGYj0UkFqAyYclRERERGRPDGyJiIiIiIhI0hjYEhFJimP6KbDRnYiIiJwZA1siIiIiIiKSNAa2LsDZ3kMlx3GGOWQ1y+D40hARERFRXcDAlnS45NSgNZhbB5ZWlTMOoiTl8y3hohMRERGRDTGwJcnRDMSkHJS5KqmN6ExEREREzo+BLelwxlZF6RCn8sQ8R1r7csFrw/m+D05XICIiIiKHY2BL5CDOFzCRNDiomwKvVyIiInJiDGyd2JIlSxAZGQlvb2/ExsZi165dji4SERERERGR02Fg66R+/PFHTJ48GdOnT8fRo0fxwAMP4KGHHkJWVpaji0ZERERERORUPBxdANJv/vz5mDBhAv71r38BABYsWIBNmzZh6dKlmD17tkV5PbYszepytHhno9XbimHH2ZsOKaOhfX74x0mM7dnC4Hb3SsrVfz/99T5sfbN/rcuSW6Q0O+3b647j7XXHTab7KzsX94U3BADcyC9WLz9w6Y7lBQTw3wPZ+O+BbJ3l0TM2WZVfFRmACxZvNfPXE5jxcHQt9qtf2oVbNs9Tn4KiMvXfE749gBXPdtdaX6kxotpf2blG87p8u9Ds/SZnAaONrC+v1O0irayoMDt/IiIiotpgi60TKisrw+HDhzF48GCt5YMHD8bevXsdVCoyR0m54aD3x4NZWJV2Wf354u1iq4PySWuOWLWduR5evBdTf0pHi3c2osxpYxN3q7b6Nu0y2k5PMjv9C98fNivd9fxSRNr5IUuraRtRVln9OfWM9oOdHw9m4V5p9Ql7ePFeoyOHX88vRccPkg2uf2xZ9f1mm8IdXT5K1ZvO0HGXlAsYvWSP1jJTn2uyND0RERG5JrbYOqFbt26hoqICISEhWstDQkKgUCj0blNaWorS0lL154KCAruWkYzrmpiMA9MHqD9fzy8x2FI6ZP42/PFKH7PzHrJgZ63LZ451R66Ksh9HKKsQMOOXv/DesA5G03X7eItF+QoAJn1/AP9+vGstSqfftPXHUGEgSH32mzTMHBltVmt8TYVlFfgs+SQmD2irtXxB6lmUlGtE0ZDpTfva2qNGh7M6kpWHTcevon9UMLaezsGRrDyD62uyNH1doVQqtf5L9sX6FhfrW1ysb3Gxnh2Lga0Tk9UYNlcQBJ1lKrNnz8bMmTPFKBaZIbekDElJ1a2C5/JlMNTCeCbnrlZaUy7eNpwXme+3w5cRI8s0mia/1PK63pqhsOh8miv5mOGy7D53Ez8lbTO43pSf911A29Lz2suO6t9fzbRbM0zX0XdbDqPkooCfLupPq1pfk6Xp65qUlBRHF8GlsL7FxfoWF+tbHEVFRY4ugktjYOuEGjVqBHd3d53W2ZycHJ1WXJVp06ZhypQp6s8FBQUIDw+3aznJsIbenkhI0G6xXXRKf0tru2A/JCSY32L777M7cfF2Sa3L6OpGxjZHQoLxFtsPjmxBfmml0TQ19W8fioQE27fY7io5hp+P6u+x0adNY4xJiDZ4jZnyaM9WSKjRYnvW6ywWb880mXZTwVEknbxpNP+nB8aif1QwvE/nYM+adIPra7I0fV2hVCqRkpKCQYMGQS6XO7o4dR7rW1ysb3GxvsXFHpOOxcDWCXl6eiI2NhYpKSl45JFH1MtTUlLw8MMP693Gy8sLXl5eYhWRTDiaOFTrc0QjOT79Rye9XUU3TelnUd5b3xwgyoBZ/4hpWme7I3u6yzBz1H0m0/018yGL6loGYMm47ibTWePzf8ZiQ/pGvd2Rv30uDgD0XmPuMhjswgwAvp7ueHNoR53lbw7tiJV7s1GofslagK+nh07aJeO6I/KdjQa7I8dEBGBIp6YAgCGdmiImIlOre7Hm+posTV/XyOVy/hAVEetbXKxvcbG+xcE6diwGtk5qypQpGDduHLp164a4uDh89dVXyMrKwosvvujoopFBFWjo7akT1Kr88/4IPNi2McYs3YMreaVoF1Ifya/3tWpPmXOGYcDn23Dhlm6Xl6iQ+jh74x4sa2esuhlEN2uA8MB6+NcDLXFfeEPMG9MFE749gNQzVS1yMgCdwhrg2DVneCJZAWNdYGWATrDVwNsd/+jazKJRkTPnDEOXxGTklVQFeG6A3rodHh2KRU/Fmp2vNS7MHoa3fkrH+iNXIciA+LaNtUZFVl1jW07dwK17pegfFVx1HjedxqaTClRUCrh5rxSN63vB3U2GIR1DMXVIlMH9nfxwKOZtOo3kE9cRKS/A4v8bojfdpTnD8PLqw/jjRFWLsgeAqLAGeH1QGwxoH6qVdv2k3kjNUGD7mZuIb9dYZ31NlqYnIiIi1yQTBGNjZpIjLVmyBHPnzsX169cRHR2NL774Ag8++KBZ2xYUFMDf3x+3bt1CUFCQnUtKSqUSSUlJSEhI4NM6EbC+xcX6FhfrW1ysb3GxvsXF+haX6vd3fn4+GjRo4OjiuBy22DqxSZMmYdKkSY4uBhERERERkVPjPLZEREREREQkaQxsiYiIiIiISNIY2BIREREREZGkMbAlIiIiIiIiSWNgS0RERERERJLGwJaIiIiIiIgkjYEtERERERERSRrnsa2jBEEAANy9e5cTcotAqVSiqKgIBQUFrG8RsL7FxfoWF+tbXKxvcbG+xcX6FldBQQGA6t/hJC4GtnXU7du3AQCRkZEOLgkRERERkeu4e/cu/P39HV0Ml8PAto4KDAwEAGRlZfGLJYKCggKEh4cjOzsbDRo0cHRx6jzWt7hY3+JifYuL9S0u1re4WN/iEgQBd+/eRVhYmKOL4pIY2NZRbm5Vr0/7+/vzRiaiBg0asL5FxPoWF+tbXKxvcbG+xcX6FhfrWzxsUHIcDh5FREREREREksbAloiIiIiIiCSNgW0d5eXlhRkzZsDLy8vRRXEJrG9xsb7FxfoWF+tbXKxvcbG+xcX6JlciEzgeNREREREREUkYW2yJiIiIiIhI0hjYEhERERERkaQxsCUiIiIiIiJJY2BLREREREREksbA1kktWbIEkZGR8Pb2RmxsLHbt2qVeJwgCEhMTERYWBh8fH8THx+PkyZMm8zx+/Dj69u0LHx8fNG3aFB9++CFqjh22Y8cOxMbGwtvbGy1btsSyZctsfmzOyFh9A0BGRgZGjhwJf39/+Pn5oWfPnsjKyjKaJ+tb186dOzFixAiEhYVBJpPhl19+Ua9TKpV4++230alTJ/j6+iIsLAxPP/00rl27ZjJf1rV+xuobAO7du4eXX34ZzZo1g4+PD9q3b4+lS5eazJf1bRzv3+Li/VscvH+Lj/dwIgsJ5HTWrl0ryOVyYfny5cKpU6eE1157TfD19RUuX74sCIIgzJkzR/Dz8xPWrVsnHD9+XPjnP/8pNGnSRCgoKDCYZ35+vhASEiI8/vjjwvHjx4V169YJfn5+wueff65Oc/HiRaFevXrCa6+9Jpw6dUpYvny5IJfLhZ9//tnux+xIpur7/PnzQmBgoPDmm28KR44cES5cuCD88ccfwo0bNwzmyfrWLykpSZg+fbqwbt06AYCwYcMG9bq8vDxh4MCBwo8//iicPn1aSEtLE3r06CHExsYazZN1bZix+hYEQfjXv/4ltGrVSti2bZtw6dIl4csvvxTc3d2FX375xWCerG/jeP8WF+/f4uH9W3y8hxNZhoGtE+revbvw4osvai2LiooS3nnnHaGyslIIDQ0V5syZo15XUlIi+Pv7C8uWLTOY55IlSwR/f3+hpKREvWz27NlCWFiYUFlZKQiCILz11ltCVFSU1nYvvPCC0LNnT1scltMyVt+CIAj//Oc/haeeesqiPFnfpun7n3RNBw4cEACof6Tqw7o2j7767tixo/Dhhx9qLYuJiRHee+89g/mwvo3j/VtcvH87Bu/f4uM9nMg0dkV2MmVlZTh8+DAGDx6stXzw4MHYu3cvLl26BIVCobXey8sLffv2xd69e9XLnnnmGcTHx6s/p6WloW/fvloTdA8ZMgTXrl1DZmamOk3N/Q4ZMgSHDh2CUqm04VE6D1P1XVlZiY0bN6Jt27YYMmQIgoOD0aNHD53uQKxv+8jPz4dMJkNAQIB6Gevadvr06YPffvsNV69ehSAI2LZtG86ePYshQ4ao07C+zcf7t7h4/3ZuvH/bH+/hRNoY2DqZW7duoaKiAiEhIVrLQ0JCoFAooFAo1J/1rVdp0qQJIiIi1J8VCoXebVTrjKUpLy/HrVu3anlkzslUfefk5ODevXuYM2cOhg4dis2bN+ORRx7B6NGjsWPHDnV61rftlZSU4J133sHYsWPRoEED9XLWte385z//QYcOHdCsWTN4enpi6NChWLJkCfr06aNOw/o2H+/f4uL923nx/i0O3sOJtHk4ugCkn0wm0/osCILWMlPrZ8+ebVaeNZebk6YuMlSflZWVAICHH34Yr7/+OgCgS5cu2Lt3L5YtW4a+ffsCYH3bmlKpxOOPP47KykosWbJEax3r2nb+85//YN++ffjtt9/QvHlz7Ny5E5MmTUKTJk0wcOBAAKxva/D+LS7ev50L79/i4T2cSBsDWyfTqFEjuLu7az29B4CcnByEhIQgNDQUQNXTtCZNmuisNyQ0NFRvnkD1kzpDaTw8PBAUFGT9QTkxU/XdqFEjeHh4oEOHDlrr27dvj927dxvMl/VtPaVSiTFjxuDSpUvYunWr1tN+fVjX1ikuLsa7776LDRs2YNiwYQCAzp07Iz09HZ9//rn6R1FNrG/DeP8WF+/fzof3b/HwHk6ki12RnYynpydiY2ORkpKitTwlJQW9evVCZGQkQkNDtdaXlZVhx44d6NWrl8F84+LisHPnTpSVlamXbd68GWFhYWjRooU6Tc39bt68Gd26dYNcLrfB0TkfU/Xt6emJ+++/H2fOnNFaf/bsWTRv3txgvqxv66h+FJ07dw5btmwx63+grGvrKJVKKJVKuLlp/2/A3d1d3dKlD+vbMN6/xcX7t3Ph/VtcvIcT6SHeOFVkLtX0BStWrBBOnTolTJ48WfD19RUyMzMFQaiaLsLf319Yv369cPz4ceGJJ57QmS7inXfeEcaNG6f+nJeXJ4SEhAhPPPGEcPz4cWH9+vVCgwYN9A7v/vrrrwunTp0SVqxY4RLDu5uq7/Xr1wtyuVz46quvhHPnzgkLFy4U3N3dhV27dqnzYH2b5+7du8LRo0eFo0ePCgCE+fPnC0ePHhUuX74sKJVKYeTIkUKzZs2E9PR04fr16+p/paWl6jxY1+YzVt+CIAh9+/YVOnbsKGzbtk24ePGi8O233wre3t7CkiVL1Hmwvi3D+7e4eP8WD+/f4uM9nMgyDGyd1OLFi4XmzZsLnp6eQkxMjLBjxw71usrKSmHGjBlCaGio4OXlJTz44IPC8ePHtbYfP3680LdvX61lx44dEx544AHBy8tLCA0NFRITE9VDu6ts375d6Nq1q+Dp6Sm0aNFCWLp0qd2O0ZkYq29BEIQVK1YIrVu3Fry9vYX77rtPZ4441rd5tm3bJgDQ+Td+/Hjh0qVLetcBELZt26bOg3VtPmP1LQiCcP36deGZZ54RwsLCBG9vb6Fdu3bCvHnztOqO9W053r/Fxfu3OHj/Fh/v4USWkQnC32+DExEREREREUkQ37ElIiIiIiIiSWNgS0RERERERJLGwJaIiIiIiIgkjYEtERERERERSRoDWyIiIiIiIpI0BrZEREREREQkaQxsiYiIiIiISNIY2BIRERmQmJiILl26iL7f7du3QyaTQSaTYdSoUaLvX6VFixbqcuTl5TmsHERERKYwsCUiIpekCtgM/XvmmWfwxhtvIDU11WFlPHPmDFauXKn+HB8fj8mTJ+uk++WXXyCTydRpjB1XixYtAAAKhQKvvPIKWrZsCS8vL4SHh2PEiBFax3vw4EGsW7fOnodIRERkEx6OLgAREZEjXL9+Xf33jz/+iA8++ABnzpxRL/Px8UH9+vVRv359RxQPABAcHIyAgACLtlm/fj3KysoAANnZ2ejevTu2bNmCjh07AgDc3d2RmZmJ3r17IyAgAHPnzkXnzp2hVCqxadMmvPTSSzh9+jQAoHHjxggMDLTpMREREdkDW2yJiMglhYaGqv/5+/tDJpPpLKvZFfmZZ57BqFGjMGvWLISEhCAgIAAzZ85EeXk53nzzTQQGBqJZs2b45ptvtPZ19epV/POf/0TDhg0RFBSEhx9+GJmZmXY5rsDAQPUxNG7cGAAQFBSktWzSpEmQyWQ4cOAAHn30UbRt2xYdO3bElClTsG/fPruUi4iIyJ4Y2BIREVlg69atuHbtGnbu3In58+cjMTERw4cPR8OGDbF//368+OKLePHFF5GdnQ0AKCoqQr9+/VC/fn3s3LkTu3fvRv369TF06FB1y6qY7ty5g+TkZLz00kvw9fXVWW9pCzEREZEzYGBLRERkgcDAQPznP/9Bu3bt8Nxzz6Fdu3YoKirCu+++izZt2mDatGnw9PTEnj17AABr166Fm5sbvv76a3Tq1Ant27fHt99+i6ysLGzfvl308p8/fx6CICAqKkr0fRMREdkL37ElIiKyQMeOHeHmVv1cOCQkBNHR0erP7u7uCAoKQk5ODgDg8OHDOH/+PPz8/LTyKSkpwYULF8QptAZBEABAPdgUERFRXcDAloiIyAJyuVzrs0wm07ussrISAFBZWYnY2FisWbNGJy/VO7DmatCgAfLz83WW5+XloUGDBmbl0aZNG8hkMmRkZDh0KiEiIiJbYldkIiIiO4qJicG5c+cQHByM1q1ba/3z9/e3KK+oqCgcOnRIZ/nBgwfRrl07s/IIDAzEkCFDsHjxYhQWFuqs53y1REQkRQxsiYiI7OjJJ59Eo0aN8PDDD2PXrl24dOkSduzYgddeew1XrlyxKK9JkybhwoULeOmll/DXX3/h7NmzWLx4MVasWIE333zT7HyWLFmCiooKdO/eHevWrcO5c+eQkZGB//znP4iLi7P0EImIiByOgS0REZEd1atXDzt37kRERARGjx6N9u3b47nnnkNxcbHZ3YdVWrRogV27duHChQsYPHgw7r//fqxcuRIrV67EY489ZnY+kZGROHLkCPr164epU6ciOjoagwYNQmpqKpYuXWrpIRIRETmcTFCNIkFEREROYfv27ejXrx9yc3MdPv2OM5WFiIjIELbYEhEROalmzZrhiSeecNj+O3bsiIceeshh+yciIjIXW2yJiIicTHFxMa5evQoAqF+/PkJDQx1SjsuXL0OpVAIAWrZsqTXNERERkTNhYEtERERERESS9v9ClzziBzGLBAAAAABJRU5ErkJggg==", - "text/html": [ - "\n", - "
\n", - "
\n", - " Figure\n", - "
\n", - " \n", - "
\n", - " " - ], - "text/plain": [ - "Canvas(toolbar=Toolbar(toolitems=[('Home', 'Reset original view', 'home', 'home'), ('Back', 'Back to previous …" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", - "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", - "\n", - "for i,v in enumerate(variables_to_plot):\n", - " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", - " ts_ax.grid()\n", - "\n", - "plt.show()\n" - ] - }, - { - "cell_type": "markdown", - "id": "194399aa-1907-452b-8ba9-bc31d7f60291", - "metadata": {}, - "source": [ - "## Quality check plots\n", - "#### Define variable for QC plot" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", - "metadata": {}, - "outputs": [], - "source": [ - "qc_variable = 'CloudLayerTopHeightMplCamp'" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "QC not available for the selected field: CloudLayerTopHeightMplCamp\n" - ] - } - ], - "source": [ - "# QC Plot\n", - "if ('qc_'+qc_variable) in ds.variables:\n", - "\n", - " # Plot\n", - " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", - " qc_display.add_subplots((2,), figsize = (9.5,10))\n", - " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", - " qc_ax.grid()\n", - " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", - "\n", - " plt.show()\n", - "else:\n", - " print(f'QC not available for the selected field: {qc_variable}')\n" - ] - }, - { - "cell_type": "markdown", - "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", - "metadata": {}, - "source": [ - "## Field selection dropdown menu\n", - "Select variable to be plotted from a dropdown menu" - ] - }, - { - "cell_type": "code", - "execution_count": 116, - "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/home/kefeimo/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/act/plotting/plot.py:81: UserWarning: Could not discern datastreamname and dict or tuple were not provided. Using defaultname of act_datastream!\n", - " warnings.warn(\n" - ] - }, - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "29438b2a84d24cb3b3d1d4be9f4dae0c", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "AppLayout(children=(Dropdown(description='Field:', layout=Layout(grid_area='header', margin='0px 30% 0px 20%',…" - ] - }, - "execution_count": 116, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "plt.ioff()\n", - "\n", - "# populate dropdown menu with available variables \n", - "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", - "d_variable = 'CloudBaseBestEstimate'\n", - "dropdown = widgets.Dropdown(\n", - " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", - " value= d_variable,\n", - " description='Field:',\n", - " disabled=False,\n", - ")\n", - "dropdown.layout.margin = '0px 30% 0px 20%'\n", - "dropdown.layout.width = '50%'\n", - "\n", - "# set up display\n", - "i_display = act.plotting.TimeSeriesDisplay(ds)\n", - "i_display.add_subplots((1,), figsize = (9.5,5))\n", - "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", - "i_ax.grid()\n", - "i_fig = i_display.fig\n", - "\n", - "# update plot callback function\n", - "def update_plot(change):\n", - " i_ax.cla()\n", - " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", - " i_ax_new.grid()\n", - " i_fig.canvas.draw()\n", - " i_fig.canvas.flush_events()\n", - "\n", - "dropdown.observe(update_plot, names='value')\n", - "\n", - "widgets.AppLayout(\n", - " header=dropdown,\n", - " center=i_fig.canvas,\n", - " pane_heights=[1, 6,1]\n", - ")\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "1d7932f0", - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.16" - }, - "widgets": { - "application/vnd.jupyter.widget-state+json": { - "state": {}, - "version_major": 2, - "version_minor": 0 - } - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/VAPs/quicklook/BBHRP/.ipynb_checkpoints/bbhrpavg1mlawer.c1-checkpoint.ipynb b/VAPs/quicklook/BBHRP/.ipynb_checkpoints/bbhrpavg1mlawer.c1-checkpoint.ipynb deleted file mode 100644 index 3ad7672a..00000000 --- a/VAPs/quicklook/BBHRP/.ipynb_checkpoints/bbhrpavg1mlawer.c1-checkpoint.ipynb +++ /dev/null @@ -1,3768 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "70840257-70e4-45e2-b491-14bff5a257a3", - "metadata": {}, - "source": [ - "# BBHRPAVG1MLAWER.C1 Plots\n", - "\n", - "[Click here](https://www.arm.gov/capabilities/vaps/bbhrp) for more information about this vap." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "460fd89f-e034-452c-b837-f65c5958264f", - "metadata": {}, - "outputs": [], - "source": [ - "%matplotlib widget\n", - "import ipywidgets as widgets\n", - "\n", - "import matplotlib.pyplot as plt\n", - "import ipywidgets as widgets\n", - "import numpy as np\n", - "import pandas as pd\n", - "import os\n", - "from datetime import datetime\n", - "\n", - "import act\n", - "import xarray as xr\n", - "\n", - "# Data archive directory\n", - "DATA_DIR = r'/data/archive/'\n", - "\n", - "# Datastream info\n", - "DATASTREAM_NAME = 'bbhrpavg1mlawer'\n", - "DATA_LEVEL = 'c1'\n", - "LOCATIONS = [{'end_date': '2006-02-27', 'facility': 'C1', 'site': 'sgp', 'start_date': '2000-03-01'}]" - ] - }, - { - "cell_type": "markdown", - "id": "9faaf875", - "metadata": {}, - "source": [ - "## Define site, facility, and date range" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "ac6764f5", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "The following locations and date ranges are available for this VAP:\n" - ] - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
sitefacilitystart_dateend_date
0sgpC12000-03-012006-02-27
\n", - "
" - ], - "text/plain": [ - " site facility start_date end_date\n", - "0 sgp C1 2000-03-01 2006-02-27" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "print(\"The following locations and date ranges are available for this VAP:\")\n", - "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" - ] - }, - { - "cell_type": "markdown", - "id": "8d132223", - "metadata": {}, - "source": [ - "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "e563983a", - "metadata": {}, - "outputs": [], - "source": [ - "site_facility = ( 'sgp', 'C1' )\n", - "\n", - "date_start = '2006-02-25'\n", - "date_end = '2006-02-27'" - ] - }, - { - "cell_type": "markdown", - "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", - "metadata": {}, - "source": [ - "## Load data files\n", - "Load data files from /data/archive/" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'/data/archive/sgp/sgpbbhrpavg1mlawerC1.c1'" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Compile list of files\n", - "site, facility = site_facility\n", - "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", - "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", - "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", - "dir_path\n" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "6be8f3dc", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "['20060225', '20060226', '20060227']" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "from datetime import date, timedelta\n", - "import pandas as pd\n", - "\n", - "def get_ARM_formated_dates(start_date, end_date):\n", - " \"\"\"\n", - " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", - " EXAMPLE:\n", - " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", - " >> [\"20180219\", \"20180220\", \"20180221\"] \n", - " \"\"\"\n", - " \n", - " _start_date = pd.to_datetime(start_date)\n", - " _end_date = pd.to_datetime(end_date)\n", - " \n", - " delta = _end_date - _start_date # returns timedelta \n", - " dates = []\n", - "\n", - " for i in range(delta.days + 1):\n", - " day = _start_date + timedelta(days=i)\n", - " day_formated = day.strftime(format=\"%Y%m%d\")\n", - " dates.append(day_formated)\n", - " return dates\n", - "\n", - "\n", - "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "51feea2e", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "['/data/archive/sgp/sgpbbhrpavg1mlawerC1.c1/sgpbbhrpavg1mlawerC1.c1.20060225.002000.cdf',\n", - " '/data/archive/sgp/sgpbbhrpavg1mlawerC1.c1/sgpbbhrpavg1mlawerC1.c1.20060227.002000.cdf']" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Filter a list of files based on date pattern\n", - "import glob\n", - "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", - "files_filter = []\n", - "for date in dates:\n", - " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", - " files_filter\n", - "files_filter" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "id": "b0e5d0d7", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "2 files loaded\n" - ] - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "
<xarray.Dataset>\n",
-       "Dimensions:                               (time: 96, levels: 55, layers: 54)\n",
-       "Coordinates:\n",
-       "  * time                                  (time) datetime64[ns] 2006-02-25T00...\n",
-       "Dimensions without coordinates: levels, layers\n",
-       "Data variables: (12/52)\n",
-       "    base_time                             (time) datetime64[ns] 2006-02-25T00...\n",
-       "    time_offset                           (time) datetime64[ns] 2006-02-25T00...\n",
-       "    height                                (time, levels) float32 dask.array<chunksize=(48, 55), meta=np.ndarray>\n",
-       "    pressure                              (time, levels) float32 dask.array<chunksize=(48, 55), meta=np.ndarray>\n",
-       "    temperature                           (time, levels) float32 dask.array<chunksize=(48, 55), meta=np.ndarray>\n",
-       "    column_ozone                          (time) float32 dask.array<chunksize=(48,), meta=np.ndarray>\n",
-       "    ...                                    ...\n",
-       "    cloud_tot_lwp                         (time) float32 dask.array<chunksize=(48,), meta=np.ndarray>\n",
-       "    cloud_tot_iwp                         (time) float32 dask.array<chunksize=(48,), meta=np.ndarray>\n",
-       "    cloud_fraction                        (time) float32 dask.array<chunksize=(48,), meta=np.ndarray>\n",
-       "    lat                                   (time) float32 36.61 36.61 ... 36.61\n",
-       "    lon                                   (time) float32 -97.49 ... -97.49\n",
-       "    alt                                   (time) float32 315.0 315.0 ... 315.0\n",
-       "Attributes:\n",
-       "    Date:                 Thu Jun  4 22:13:53 2009\n",
-       "    Version:              Version: ver1.5\n",
-       "    missing_value:        -9999.0\n",
-       "    _file_dates:          ['20060225', '20060227']\n",
-       "    _file_times:          ['002000', '002000']\n",
-       "    datastream:           sgpbbhrpavg1mlawerC1.c1\n",
-       "    _datastream:          sgpbbhrpavg1mlawerC1.c1\n",
-       "    _arm_standards_flag:  1
" - ], - "text/plain": [ - "\n", - "Dimensions: (time: 96, levels: 55, layers: 54)\n", - "Coordinates:\n", - " * time (time) datetime64[ns] 2006-02-25T00...\n", - "Dimensions without coordinates: levels, layers\n", - "Data variables: (12/52)\n", - " base_time (time) datetime64[ns] 2006-02-25T00...\n", - " time_offset (time) datetime64[ns] 2006-02-25T00...\n", - " height (time, levels) float32 dask.array\n", - " pressure (time, levels) float32 dask.array\n", - " temperature (time, levels) float32 dask.array\n", - " column_ozone (time) float32 dask.array\n", - " ... ...\n", - " cloud_tot_lwp (time) float32 dask.array\n", - " cloud_tot_iwp (time) float32 dask.array\n", - " cloud_fraction (time) float32 dask.array\n", - " lat (time) float32 36.61 36.61 ... 36.61\n", - " lon (time) float32 -97.49 ... -97.49\n", - " alt (time) float32 315.0 315.0 ... 315.0\n", - "Attributes:\n", - " Date: Thu Jun 4 22:13:53 2009\n", - " Version: Version: ver1.5\n", - " missing_value: -9999.0\n", - " _file_dates: ['20060225', '20060227']\n", - " _file_times: ['002000', '002000']\n", - " datastream: sgpbbhrpavg1mlawerC1.c1\n", - " _datastream: sgpbbhrpavg1mlawerC1.c1\n", - " _arm_standards_flag: 1" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Load files as a single dataset\n", - "files_list = files_filter \n", - "ds = act.io.armfiles.read_netcdf(files_list)\n", - "ds.clean.cleanup()\n", - "print(f'{len(files_list)} files loaded')\n", - "ds\n" - ] - }, - { - "cell_type": "markdown", - "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", - "metadata": {}, - "source": [ - "## Plot time series data\n", - "#### Define the list of variables to be plotted:" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "id": "d09b789e-84f1-4605-846b-a72c110c8048", - "metadata": {}, - "outputs": [], - "source": [ - "variables_to_plot = ['pressure', 'temperature', 'column_ozone']" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", - "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "901cca0c06a4463aa7bea7658c637e66", - "version_major": 2, - "version_minor": 0 - }, - "image/png": "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NXm/7du361e/+pUOPvhgBQIBdezYUWeddZY++eSTJvPOmjVL3bt3V+vWrTVgwAC9/fbb+9wWkvTvf/9b55xzjtq0aaOcnBwdc8wxmjdvXsQ8VvGRpKVLl8rn82np0qWSpMGDB+tvf/ubNmzYELHdpB8um545c6buuOMOHXTQQcrJyVHfvn2bbKPLL79c3bp1a9LWvT8/Pp9P33//vebNmxde1uDBg22t95729Xmqr69Xx44ddemllzZ57fbt25Wbm6uJEyeGp1VWVuqGG25Q9+7dlZ2drQMPPFATJkzgCgoA8Ag9tgCQIv7zn/9Ikjp06BCeVldXpx//+McaN26cbr75ZjU0NCgUCumcc87RG2+8oRtvvFEDBw7Uhg0bNGXKFA0ePFjvvvuucnNztX79eg0fPlwnnXSSHnroIR1wwAH68ssv9fLLL6uurk55eXl64oknNH78eP3iF7/Q3XffLb/fr//85z9as2bNfq/H+++/r48//li//vWv1b17d7Vq1UoVFRXq16+f/H6/Jk+erEMOOURvvfWWpk+frvXr12vu3LnNvufatWt11llnacKECWrVqpU++eQT3XXXXXrnnXf06quvSpJuu+02ff/993rmmWf01ltvhV/bqVOnqO/76KOP6rLLLtM555yjefPmKSsrS3/84x91+umna9GiReEfEjZt2qR27drpzjvvVIcOHbRt2zbNmzdP/fv318qVK3XYYYdJkqqqqjRo0CCtX79eN910k/r3768dO3bo9ddf1+bNm9WrV6/wsu+55x716tUrfAnwbbfdprPOOkvr1q1TYWFh1DZ/+umnGjhwoDp27Kj//d//Vbt27fToo4/q8ssv11dffaUbb7yx+QDt5d5779XPfvYz/fe//9Xzzz9vOc+cOXPUtWtXzZ49W6FQSDNnztSZZ56pZcuWacCAAY6W99Zbb+mUU07RkCFDdNttt0mSCgoKHL2Hnc9TVlaWLrnkEt1///265557Ipbx+OOPq6amRmPHjpW060qI0tJSffHFF5o0aZL69Omj1atXa/LkyVq1apVeeeUVyx93AAAuMgCApDJ37lwjybz99tumvr7eVFVVmZdeesl06NDB5Ofnm4qKCmOMMWPGjDGSzEMPPRTx+scff9xIMs8++2zE9BUrVhhJ5t577zXGGPPMM88YSeaDDz6I2pZrr73WHHDAAc22d8qUKcbqcNO4HuvWrQtP69q1q8nIyDCffvppxLzjxo0zrVu3Nhs2bIiYfvfddxtJZvXq1c22YU+hUMjU19ebZcuWGUnmww8/DD93zTXXWLbVyvfff2/atm1rRowYETE9GAyao48+2vTr1y/qaxsaGkxdXZ3p0aOHuf7668PTp02bZiSZJUuWRH3tunXrjCRz1FFHmYaGhvD0d955x0gyjz/+eLPtvuiii0wgEDCff/55xPQzzzzT5OXlme3btxtjrONjjDGvvfaakWRee+218LThw4ebrl27Rm1rSUmJ2blzZ3h6ZWWladu2rTnttNPC08aMGWP5Hlafn1atWpkxY8Y0u557kmSmTJkS/tvu5+mjjz4ykswDDzwQMV+/fv3McccdF/57xowZxu/3mxUrVkTM1/gdWrhwYXha165dHbUdAGAPlyIDQJI64YQTlJWVpfz8fJ199tkqLi7W3//+dxUVFUXM95Of/CTi75deekkHHHCARowYoYaGhvDjmGOOUXFxcfgS02OOOUbZ2dn62c9+pnnz5umzzz5r0oZ+/fpp+/btGjVqlP76179q69atMa9Xnz591LNnzyZtHjJkiEpKSiLafOaZZ0qSli1b1ux7fvbZZxo9erSKi4uVkZGhrKwslZaWSpI+/vjj/Wrn8uXLtW3bNo0ZMyaiTaFQSGeccYZWrFgRvgy1oaFB5eXl6t27t7Kzs5WZmans7GytXbs2Yvl///vf1bNnT5122mn7XP7w4cOVkZER/rtPnz6SfrgUPZpXX31Vp556qrp06RIx/fLLL1d1dXVEb7VbRo4cqZycnPDf+fn5GjFihF5//XUFg0HXl7cvdj9PRx11lI477riIKwI+/vhjvfPOO7riiisi3u/II4/UMcccE/F+p59+esRl2wCA+OFSZABIUo888ogOP/xwZWZmqqioyPKS2by8vCaXaX711Vfavn27srOzLd+3MTk95JBD9Morr2jmzJm65ppr9P333+vggw/WL3/5S1133XWSpEsvvVQNDQ3605/+pJ/85CcKhUI6/vjjNX36dA0dOnS/1stqPb766iu9+OKLysrKarbNVnbs2KGTTjpJOTk5mj59unr27Km8vDxt3LhRI0eO1M6dO/ernV999ZUk6fzzz486z7Zt29SqVStNnDhR99xzj2666SaVlpaqTZs28vv9uuqqqyKW//XX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EO/0QrzTB7FOF1VVVSosLIz6fHZ2toqLi/VmxcJm36e4uFjZ2dluNy9lkdi2UBMnTtSll16qvn37asCAAXrggQf0+eef6+c//7mt15eUlGjjxo3Kz8+PevkyklfjpeYbN25UQUFBopuDOCLW6YV4pxfinT6IdXowxqiqqkolJSXNzpeTk6N169aprq753vvs7Gzl5OS42cSURmLbQl144YX65ptvNG3aNG3evFlHHnmkFi5cqK5du9p6vd/vV+fOnePcSiRaQUEBB8g0QazTC/FOL8Q7fRDr1NdcT+2ecnJySFpdRmLbgo0fP17jx49PdDMAAAAAoEWj5CkAAAAAIKmR2AJJKBAIaMqUKRGVsJGaiHV6Id7phXinD2INxJ/PcJMkAAAAAEASo8cWAAAAAJDUSGwBAAAAAEmNxBYAAAAAkNRIbAEPvf766xoxYoRKSkrk8/m0YMGC8HP19fW66aabdNRRR6lVq1YqKSnRZZddpk2bNu3zfVetWqXS0lLl5ubqwAMP1LRp07T38Plly5bpuOOOU05Ojg4++GDdf//9bq8eLNx7773q3r27cnJydNxxx+mNN94IP2eMUVlZmUpKSpSbm6vBgwdr9erV+3xP4t0y8f1OL3y30wffbSBJGACeWbhwobn11lvNs88+aySZ559/Pvzc9u3bzWmnnWaefPJJ88knn5i33nrL9O/f3xx33HHNvud3331nioqKzEUXXWRWrVplnn32WZOfn2/uvvvu8DyfffaZycvLM9ddd51Zs2aN+dOf/mSysrLMM888E69VhTHmiSeeMFlZWeZPf/qTWbNmjbnuuutMq1atzIYNG4wxxtx5550mPz/fPPvss2bVqlXmwgsvNJ06dTKVlZVR35N4t1x8v9MH3+30wncbSA4ktkCC7H1wtPLOO+8YSeGTJSv33nuvKSwsNDU1NeFpM2bMMCUlJSYUChljjLnxxhtNr169Il43btw4c8IJJ+z/CmCf+vXrZ37+859HTOvVq5e5+eabTSgUMsXFxebOO+8MP1dTU2MKCwvN/fffH/U9iXdy4Pud2vhupy++20DLxaXIQAv23Xffyefz6YADDghPu/zyyzV48ODw32+99ZZKS0sj7o13+umna9OmTVq/fn14nmHDhkW89+mnn653331X9fX18VyFtFVXV6f33nuvyXYfNmyYli9frnXr1qmioiLi+UAgoNLSUi1fvjw8jXinLr7fyYnvNvaF7zaQGCS2QAtVU1Ojm2++WaNHj1ZBQUF4eqdOnXTQQQeF/66oqFBRUVHEaxv/rqioaHaehoYGbd26NV6rkNa2bt2qYDBoud0rKirCsYn2fCPinZr4ficvvttoDt9tIHEyE90AAE3V19froosuUigU0r333hvx3IwZM5rM7/P5Iv42u4tP7Dndzjxwn9V231dc9pxGvFMP3+/UwHcbe+O7DSQWPbZAC1NfX68LLrhA69at05IlSyJ+8bVSXFwc0QsgSVu2bJH0w6+/0ebJzMxUu3btXGw9GrVv314ZGRmW272oqEjFxcWSFPX5aIh3cuP7nfz4bsMK320g8UhsgRak8cC4du1avfLKK7YOXAMGDNDrr7+uurq68LTFixerpKRE3bp1C8+zZMmSiNctXrxYffv2VVZWlqvrgF2ys7N13HHHNdnuS5Ys0cCBA9W9e3cVFxdHPF9XV6dly5Zp4MCBUd+XeCcvvt+pge829sZ3G2ghElGxCkhXVVVVZuXKlWblypVGkpk1a5ZZuXKl2bBhg6mvrzc//vGPTefOnc0HH3xgNm/eHH7U1taG3+Pmm282l156afjv7du3m6KiIjNq1CizatUq89xzz5mCggLLWwZcf/31Zs2aNebBBx/klgEeaLwlyIMPPmjWrFljJkyYYFq1amXWr19vjNl1S5DCwkLz3HPPmVWrVplRo0Y1uSUI8U4efL/TB9/t9MJ3G0gOJLaAh1577TUjqcljzJgxZt26dZbPSTKvvfZa+D3GjBljSktLI973o48+MieddJIJBAKmuLjYlJWVhW8X0Gjp0qXm2GOPNdnZ2aZbt27mvvvu82CNcc8995iuXbua7Oxs86Mf/cgsW7Ys/FwoFDJTpkwxxcXFJhAImJNPPtmsWrUq4vXEO3nw/U4vfLfTB99tIDn4jNk9Ch0AAAAAgCTEGFsAAAAAQFIjsQUAAAAAJDUSWwAAAABAUiOxBQAAAAAkNRJbAAAAAEBSI7EFAAAAACQ1ElsAAAAAQFIjsQUAAAAAJDUSWwAAAABAUiOxBQAAAAAkNRJbAAAAAEBSI7EFAAAAACQ1ElsAAAAAQFIjsQUAj/h8PluPpUuXJrqpCbNw4UKVlZUluhkx2bRpk8rKyvTBBx8kuinNGjx4sI488shENyNszZo1Kisr0/r16z1ZntPP2uWXX65u3brFrT1OlJWVyefzJboZANCikNgCgEfeeuutiMdZZ52l3NzcJtN/9KMfJbqpCbNw4UJNnTo10c2IyaZNmzR16tQWn9i2NGvWrNHUqVM9TWyT/bMGAPhBZqIbAADp4oQTToj4u0OHDvL7/U2mp5Lq6mrl5eUluhktph0AACA+6LEFgBakrq5O06dPV69evRQIBNShQweNHTtWX3/9dcR83bp109lnn62XXnpJxx57rHJzc3X44YfrpZdekiQ9/PDDOvzww9WqVSv169dP7777bsTrL7/8crVu3VqrV6/WqaeeqlatWqlDhw669tprVV1dHTGvMUb33nuvjjnmGOXm5qpNmzY6//zz9dlnn0XM13hp6+uvv66BAwcqLy9PV1xxhSTpySef1LBhw9SpU6dwW2+++WZ9//33EW265557JEVetr1+/XqtX79ePp9PDz/8cJNt5vP5Ii4pbbxM8/3339f555+vNm3a6JBDDnG0Llb+85//aOzYserRo4fy8vJ04IEHasSIEVq1alV4nqVLl+r444+XJI0dOza8Dvu65LWiokLjxo1T586dlZ2dre7du2vq1KlqaGiImG/q1Knq37+/2rZtq4KCAv3oRz/Sgw8+KGNMk/ecP3++BgwYoNatW6t169Y65phj9OCDDzaZb8WKFTrppJOUl5engw8+WHfeeadCodA+t0dNTY1uueUWde/eXdnZ2TrwwAN1zTXXaPv27RHzRVv/bt266fLLL5e06/P6//7f/5MkDRkyJLzdGuPd+Nl64403dMIJJyg3N1cHHnigbrvtNgWDwfB7Ll261PJy/r0/P8191pyw83maMGGCWrVqpcrKyiavv/DCC1VUVKT6+vrwtCeffFIDBgxQq1at1Lp1a51++ulauXKlo3YBQDoisQWAFiIUCumcc87RnXfeqdGjR+tvf/ub7rzzTi1ZskSDBw/Wzp07I+b/8MMPdcstt+imm27Sc889p8LCQo0cOVJTpkzRn//8Z5WXl+uxxx7Td999p7PPPrvJ6+vr63XWWWfp1FNP1YIFC3Tttdfqj3/8oy688MKI+caNG6cJEybotNNO04IFC3Tvvfdq9erVGjhwoL766quIeTdv3qxLLrlEo0eP1sKFCzV+/HhJ0tq1a3XWWWfpwQcf1Msvv6wJEyboqaee0ogRI8Kvve2223T++edLirxsu1OnTvu1PUeOHKlDDz1UTz/9tO6//37H67K3TZs2qV27drrzzjv18ssv65577lFmZqb69++vTz/9VJL0ox/9SHPnzpUk/frXvw6vw1VXXRX1fSsqKtSvXz8tWrRIkydP1t///nddeeWVmjFjhn76059GzLt+/XqNGzdOTz31lJ577jmNHDlSv/jFL3T77bdHzDd58mRdfPHFKikp0cMPP6znn39eY8aM0YYNG5os++KLL9Yll1yiF154QWeeeaZuueUWPfroo81uC2OMzj33XN1999269NJL9be//U0TJ07UvHnzdMopp6i2trbZ1+9t+PDhKi8vlyTdc8894e02fPjwiLZedNFFuvjii/XXv/5V559/vqZPn67rrrvO0bIk9z5rdj5PV1xxhaqrq/XUU09FvHb79u3661//qksuuURZWVmSpPLyco0aNUq9e/fWU089pb/85S+qqqrSSSedpDVr1jheTwBIKwYAkBBjxowxrVq1Cv/9+OOPG0nm2WefjZhvxYoVRpK59957w9O6du1qcnNzzRdffBGe9sEHHxhJplOnTub7778PT1+wYIGRZF544YWIZUsyv//97yOWdccddxhJ5s033zTGGPPWW28ZSea3v/1txHwbN240ubm55sYbbwxPKy0tNZLMP/7xj2bXOxQKmfr6erNs2TIjyXz44Yfh56655hpjdWhat26dkWTmzp3b5DlJZsqUKeG/p0yZYiSZyZMnR8znZF3saGhoMHV1daZHjx7m+uuvD09vjJdVW62MGzfOtG7d2mzYsCFi+t13320kmdWrV1u+LhgMmvr6ejNt2jTTrl07EwqFjDHGfPbZZyYjI8NcfPHFzS63MV7/+te/Iqb37t3bnH766c2+9uWXXzaSzMyZMyOmP/nkk0aSeeCBB8LT9o5Po65du5oxY8aE/3766aeNJPPaa69Fbetf//rXiOk//elPjd/vD2+71157zfI9rD4/0T5r0YwZM8Z07do1/LeTz9OPfvQjM3DgwIj57r33XiPJrFq1yhhjzOeff24yMzPNL37xi4j5qqqqTHFxsbngggvC0xo/4wCAH9BjCwAtxEsvvaQDDjhAI0aMUENDQ/hxzDHHqLi4uMnllcccc4wOPPDA8N+HH364pF2Xbe45nrRx+t69dZJ08cUXR/w9evRoSdJrr70WbpPP59Mll1wS0abi4mIdffTRTdrUpk0bnXLKKU2W89lnn2n06NEqLi5WRkaGsrKyVFpaKkn6+OOP7Wwex37yk59E/O10XfbW0NCg8vJy9e7dW9nZ2crMzFR2drbWrl0b0zq89NJLGjJkiEpKSiLadeaZZ0qSli1bFp731Vdf1WmnnabCwsLwdpw8ebK++eYbbdmyRZK0ZMkSBYNBXXPNNftcdnFxsfr16xcxrU+fPpaflT29+uqrkhS+lLjR//t//0+tWrXSP/7xj30u26n8/Hz9+Mc/jpg2evRohUIhvf76664vb1+cfJ7Gjh2r5cuXh3v2JWnu3Lk6/vjjw5WpFy1apIaGBl122WUR75eTk6PS0tK0rpYOAHZQPAoAWoivvvpK27dvV3Z2tuXzW7dujfi7bdu2EX83vi7a9JqamojpmZmZateuXcS04uJiSdI333wTbpMxRkVFRZZtOvjggyP+trqUc8eOHTrppJOUk5Oj6dOnq2fPnsrLy9PGjRs1cuTIJpdIu2Xvtjhdl71NnDhR99xzj2666SaVlpaqTZs28vv9uuqqq2Jah6+++kovvvhi+HLUvTXG/Z133tGwYcM0ePBg/elPfwqPx12wYIHuuOOOcBsax2N37tx5n8veO/6SFAgE9rk+33zzjTIzM9WhQ4eI6T6fT8XFxeHPj5us4rb359VLTj5PF198sW644QY9/PDDmjFjhtasWaMVK1bo3nvvjXg/SeEx2nvz++mLAIDmkNgCQAvRvn17tWvXTi+//LLl8/n5+a4ur6GhQd98801EclNRUSHph4Snffv28vl8euONNxQIBJq8x97TrO6t+eqrr2rTpk1aunRpuJdWUpMiQ83JycmRpCZjN5tLaPZui9N12dujjz6qyy67LDwWtNHWrVt1wAEHNPva5rRv3159+vTRHXfcYfl8SUmJJOmJJ55QVlaWXnrppfD2kKQFCxZEzN+YbH7xxRfq0qXLfrerOe3atVNDQ4O+/vrriOTWGKOKioqI5CwQCFiOuXWajFqNgd778xrtc7L3j0JucPJ5atOmjc455xw98sgjmj59uubOnaucnByNGjUq4v0k6ZlnnlHXrl1dby8ApDoSWwBoIc4++2w98cQTCgaD6t+/vyfLfOyxx/TLX/4y/Pf8+fMl7bqcubFNd955p7788ktdcMEF+7WMxgRz75P/P/7xj03mbZxn586dys3NDU8vKipSTk6OPvroo4j5//rXv9puR6zr4vP5mqzD3/72N3355Zc69NBDLdfBbrsWLlyoQw45RG3atGl2+ZmZmcrIyAhP27lzp/7yl79EzDds2DBlZGTovvvu04ABA2y1walTTz1VM2fO1KOPPqrrr78+PP3ZZ5/V999/r1NPPTU8rVu3bk3i9uqrr2rHjh0R0/a13aqqqvTCCy9EXI48f/58+f1+nXzyyeFlSdJHH32k008/PTzfCy+80OT9on3W7HL6eRo7dqyeeuopLVy4UI8++qjOO++8iB9ETj/9dGVmZuq///1vk8voAQD7RmILAC3ERRddpMcee0xnnXWWrrvuOvXr109ZWVn64osv9Nprr+mcc87Reeed59rysrOz9dvf/lY7duzQ8ccfr+XLl2v69Ok688wzNWjQIEnSiSeeqJ/97GcaO3as3n33XZ188slq1aqVNm/erDfffFNHHXWUrr766maXM3DgQLVp00Y///nPNWXKFGVlZemxxx7Thx9+2GTeo446SpJ011136cwzz1RGRob69Omj7OxsXXLJJXrooYd0yCGH6Oijj9Y777wTTsTtiHVdzj77bD388MPq1auX+vTpo/fee0+/+c1vmlzye8ghhyg3N1ePPfaYDj/8cLVu3VolJSXhnte9TZs2TUuWLNHAgQP1y1/+Uocddphqamq0fv16LVy4UPfff786d+6s4cOHa9asWRo9erR+9rOf6ZtvvtHdd9/dJNnu1q2bJk2apNtvv107d+7UqFGjVFhYqDVr1mjr1q2aOnWq7W0WzdChQ3X66afrpptuUmVlpU488UR99NFHmjJlio499lhdeuml4XkvvfRS3XbbbZo8ebJKS0u1Zs0azZkzR4WFhRHv2TjW9IEHHlB+fr5ycnLUvXv3cG9su3btdPXVV+vzzz9Xz549tXDhQv3pT3/S1VdfrYMOOkjSrkuTTzvtNM2YMUNt2rRR165d9Y9//EPPPfdck3Vo7rNmh9PP07Bhw9S5c2eNHz9eFRUVGjt2bMT7devWTdOmTdOtt96qzz77TGeccYbatGmjr776Su+8845atWrlSuwAIGUltnYVAKSvvasiG2NMfX29ufvuu83RRx9tcnJyTOvWrU2vXr3MuHHjzNq1a8Pzde3a1QwfPrzJe0oy11xzTcS0xoqwv/nNb5os+6OPPjKD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aQRbAAAAAIClEWwBAAAAAJZGsAUAAAAAWBrBFgAAAABgaQRbAAAAAIClEWwBAAAAAJZGsAUAAAAAWBrBFgAAAABgaQRbAAAAAIClEWwBAAAAAJZGsAUAAAAAWBrBFgAAAABgaQRbAAAAAIClEWwBAAAAAJZGsAUAAAAAWBrBFgAAAABgaQRbAAAAAIClEWwBAAAAAJZGsAUAAAAAWBrBFgAAAABgaQRbAAAAAIClEWwBAAAAAJZGsAUAAAAAWBrBFgAAAABgaQRbAAAAAIClEWwBAAAAAJZGsAUAAAAAWBrBFgAAAABgaQRbAAAAAIClEWwBAAAAAJZGsAUAAAAAWBrBFgAAAABgaQRbAAAAAIClEWwBAAAAAJZGsAUAAAAAWBrBFgAAAABgaQRbAAAAAIClEWwBAAAAAJZGsAUAAAAAWBrBFgAAAABgaQRbAAAAAIClEWwBAAAAAJZGsAUAAAAAWBrBFgAAAABgaQRbAAAAAIClEWwBAAAAAJZGsAUAAAAAWBrBFgAAAABgaQRbAAAAAIClEWwBAAAAAJZGsAUAAAAAWBrBFgAAAABgaQRbAAAAAIClEWwBAAAAAJZGsAUAAAAAWBrBFgAAAABgaQRbAAAAAIClEWwBAAAAAJZGsAUAAAAAWBrBFgAAAABgaQRbAAAAAIClEWwBAAAAAJZGsAUAAAAAWBrBFgAAAABgaQRbAAAAAIClEWwBAAAAAJZGsAUAAAAAWBrBFgAAAABgaQRbAAAAAIClEWwBAAAAAJZGsAUAAAAAWBrBFgAAAABgaQRbAAAAAIClEWwBAAAAAJZGsAUAAAAAWBrBFgAAAABgaQRbAAAAAIClEWwBAAAAAJZGsAUAAAAAWBrBFgAAAABgaQRbAAAAAIClEWwBAAAAAJZGsAUAAAAAWBrBFgAAAABgaQRbAAAAAIClEWwBAAAAAJZGsAUAAAAAWNr/B2OKQCR826BBAAAAAElFTkSuQmCC", - "text/html": [ - "\n", - "
\n", - "
\n", - " Figure\n", - "
\n", - " \n", - "
\n", - " " - ], - "text/plain": [ - "Canvas(toolbar=Toolbar(toolitems=[('Home', 'Reset original view', 'home', 'home'), ('Back', 'Back to previous …" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", - "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", - "\n", - "for i,v in enumerate(variables_to_plot):\n", - " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", - " ts_ax.grid()\n", - "\n", - "plt.show()\n" - ] - }, - { - "cell_type": "markdown", - "id": "194399aa-1907-452b-8ba9-bc31d7f60291", - "metadata": {}, - "source": [ - "## Quality check plots\n", - "#### Define variable for QC plot" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", - "metadata": {}, - "outputs": [], - "source": [ - "qc_variable = 'flux'" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", - "metadata": {}, - "outputs": [ - { - "ename": "KeyError", - "evalue": "'flux'", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mKeyError\u001b[0m Traceback (most recent call last)", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/core/dataset.py:1348\u001b[0m, in \u001b[0;36mDataset._construct_dataarray\u001b[0;34m(self, name)\u001b[0m\n\u001b[1;32m 1347\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m-> 1348\u001b[0m variable \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_variables\u001b[49m\u001b[43m[\u001b[49m\u001b[43mname\u001b[49m\u001b[43m]\u001b[49m\n\u001b[1;32m 1349\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mKeyError\u001b[39;00m:\n", - "\u001b[0;31mKeyError\u001b[0m: 'flux'", - "\nDuring handling of the above exception, another exception occurred:\n", - "\u001b[0;31mKeyError\u001b[0m Traceback (most recent call last)", - "Cell \u001b[0;32mIn[11], line 7\u001b[0m\n\u001b[1;32m 5\u001b[0m qc_display \u001b[38;5;241m=\u001b[39m act\u001b[38;5;241m.\u001b[39mplotting\u001b[38;5;241m.\u001b[39mTimeSeriesDisplay(ds)\n\u001b[1;32m 6\u001b[0m qc_display\u001b[38;5;241m.\u001b[39madd_subplots((\u001b[38;5;241m2\u001b[39m,), figsize \u001b[38;5;241m=\u001b[39m (\u001b[38;5;241m9.5\u001b[39m,\u001b[38;5;241m10\u001b[39m))\n\u001b[0;32m----> 7\u001b[0m qc_ax \u001b[38;5;241m=\u001b[39m \u001b[43mqc_display\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mplot\u001b[49m\u001b[43m(\u001b[49m\u001b[43mqc_variable\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43msubplot_index\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m0\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mset_title\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mQC results on field: \u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m \u001b[49m\u001b[38;5;241;43m+\u001b[39;49m\u001b[43m \u001b[49m\u001b[43mqc_variable\u001b[49m\u001b[43m,\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 8\u001b[0m qc_ax\u001b[38;5;241m.\u001b[39mgrid()\n\u001b[1;32m 9\u001b[0m qc_display\u001b[38;5;241m.\u001b[39mqc_flag_block_plot(qc_variable, subplot_index\u001b[38;5;241m=\u001b[39m(\u001b[38;5;241m1\u001b[39m,))\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/act/plotting/timeseriesdisplay.py:418\u001b[0m, in \u001b[0;36mTimeSeriesDisplay.plot\u001b[0;34m(self, field, dsname, subplot_index, cmap, set_title, add_nan, day_night_background, invert_y_axis, abs_limits, time_rng, y_rng, use_var_for_y, set_shading, assessment_overplot, overplot_marker, overplot_behind, overplot_markersize, assessment_overplot_category, assessment_overplot_category_color, force_line_plot, labels, cbar_label, cbar_h_adjust, secondary_y, y_axis_flag_meanings, colorbar_labels, cb_friendly, **kwargs)\u001b[0m\n\u001b[1;32m 415\u001b[0m assessment_overplot_category_color[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mAcceptable\u001b[39m\u001b[38;5;124m'\u001b[39m] \u001b[38;5;241m=\u001b[39m (\u001b[38;5;241m0.0\u001b[39m, \u001b[38;5;241m0.4240129715562796\u001b[39m, \u001b[38;5;241m0.4240129715562796\u001b[39m),\n\u001b[1;32m 417\u001b[0m \u001b[38;5;66;03m# Get data and dimensions\u001b[39;00m\n\u001b[0;32m--> 418\u001b[0m data \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_obj\u001b[49m\u001b[43m[\u001b[49m\u001b[43mdsname\u001b[49m\u001b[43m]\u001b[49m\u001b[43m[\u001b[49m\u001b[43mfield\u001b[49m\u001b[43m]\u001b[49m\n\u001b[1;32m 419\u001b[0m dim \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mlist\u001b[39m(\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_obj[dsname][field]\u001b[38;5;241m.\u001b[39mdims)\n\u001b[1;32m 420\u001b[0m xdata \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_obj[dsname][dim[\u001b[38;5;241m0\u001b[39m]]\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/core/dataset.py:1439\u001b[0m, in \u001b[0;36mDataset.__getitem__\u001b[0;34m(self, key)\u001b[0m\n\u001b[1;32m 1437\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39misel(\u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkey)\n\u001b[1;32m 1438\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m utils\u001b[38;5;241m.\u001b[39mhashable(key):\n\u001b[0;32m-> 1439\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_construct_dataarray\u001b[49m\u001b[43m(\u001b[49m\u001b[43mkey\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 1440\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m utils\u001b[38;5;241m.\u001b[39miterable_of_hashable(key):\n\u001b[1;32m 1441\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_copy_listed(key)\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/core/dataset.py:1350\u001b[0m, in \u001b[0;36mDataset._construct_dataarray\u001b[0;34m(self, name)\u001b[0m\n\u001b[1;32m 1348\u001b[0m variable \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_variables[name]\n\u001b[1;32m 1349\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mKeyError\u001b[39;00m:\n\u001b[0;32m-> 1350\u001b[0m _, name, variable \u001b[38;5;241m=\u001b[39m \u001b[43m_get_virtual_variable\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_variables\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mname\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mdims\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 1352\u001b[0m needed_dims \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mset\u001b[39m(variable\u001b[38;5;241m.\u001b[39mdims)\n\u001b[1;32m 1354\u001b[0m coords: \u001b[38;5;28mdict\u001b[39m[Hashable, Variable] \u001b[38;5;241m=\u001b[39m {}\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/core/dataset.py:186\u001b[0m, in \u001b[0;36m_get_virtual_variable\u001b[0;34m(variables, key, dim_sizes)\u001b[0m\n\u001b[1;32m 184\u001b[0m split_key \u001b[38;5;241m=\u001b[39m key\u001b[38;5;241m.\u001b[39msplit(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m.\u001b[39m\u001b[38;5;124m\"\u001b[39m, \u001b[38;5;241m1\u001b[39m)\n\u001b[1;32m 185\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mlen\u001b[39m(split_key) \u001b[38;5;241m!=\u001b[39m \u001b[38;5;241m2\u001b[39m:\n\u001b[0;32m--> 186\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mKeyError\u001b[39;00m(key)\n\u001b[1;32m 188\u001b[0m ref_name, var_name \u001b[38;5;241m=\u001b[39m split_key\n\u001b[1;32m 189\u001b[0m ref_var \u001b[38;5;241m=\u001b[39m variables[ref_name]\n", - "\u001b[0;31mKeyError\u001b[0m: 'flux'" - ] - }, - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "ae3815a803c64f629cc4a6feb7130f6b", - "version_major": 2, - "version_minor": 0 - }, - "image/png": "iVBORw0KGgoAAAANSUhEUgAAA7YAAAPoCAYAAADqfmIfAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAA9hAAAPYQGoP6dpAABCA0lEQVR4nO3df2zV9b348Veh0Kr3toswKwgy3NXJLhm7lMAot1l0WgOGG5LdwOKNqBeTNdsuAa7egdzoICbN3c3MvU7BLYJmCbrGn/GPXkdzcy8/hJuMpiyLkLtFuBa2VlLMWtTdIvD5/mHo93YtDpCe9oWPR3L+OO99PvR1trfdefL5HE9ZURRFAAAAQFJjRnoAAAAA+CSELQAAAKkJWwAAAFITtgAAAKQmbAEAAEhN2AIAAJCasAUAACA1YQsAAEBqwhYAAIDUhC0AAACpCVsAAABSE7YAAACkJmwBAABITdgCAACQmrAFAAAgNWELAABAasIWAACA1IQtAAAAqQlbAAAAUhO2AAAApCZsAQAASE3YAgAAkJqwBQAAIDVhCwAAQGrCFgAAgNSELQAAAKkJWwAAAFITtgAAAKQmbAEAAEhN2AIAAJCasAUAACA1YQsAAEBqwhYAAIDUhC0AAACpCVsAAABSE7YAAACkJmwBAABITdgCAACQmrAFAAAgNWELAABAasIWAACA1IQtAAAAqQlbAAAAUhO2AAAApCZsAQAASE3YAgAAkJqwBQAAIDVhCwAAQGrCFgAAgNSELQAAAKkJWwAAAFITtgAAAKQmbAEAAEhN2AIAAJCasAUAACA1YQsAAEBqwhYAAIDUhC0AAACpCVsAAABSE7YAAACkJmwBAABITdgCAACQmrAFAAAgNWELAABAasIWAACA1IQtAAAAqQlbAAAAUhO2AAAApCZsAQAASE3YAgAAkJqwBQAAIDVhCwAAQGrCFgAAgNSELQAAAKkJWwAAAFITtgAAAKQmbAEAAEhN2AIAAJCasAUAACA1YQsAAEBqwhYAAIDUhC0AAACpCVsAAABSE7YAAACkJmwBAABITdgCAACQmrAFAAAgNWELAABAasIWAACA1IQtAAAAqQlbAAAAUhO2AAAApCZsAQAASE3YAgAAkJqwBQAAIDVhCwAAQGrCFgAAgNSELQAAAKkJWwAAAFITtgAAAKQmbAEAAEhN2AIAAJCasAUAACA1YQsAAEBqwhYAAIDUhC0AAACpCVsAAABSE7YAAACkJmwBAABITdgCAACQmrAFAAAgNWELAABAasIWAACA1IQtAAAAqQlbAAAAUhO2w2znzp2xePHimDx5cpSVlcWrr776R8/ZsWNH1NbWRmVlZdxwww3x1FNPDf+gAAAASQnbYfb+++/HrFmz4oknnjiv4w8fPhyLFi2K+vr6aG9vj4ceeihWrlwZL7300jBPCgAAkFNZURTFSA/xaVFWVhavvPJKLFmy5JzHfPe7343XXnstDh482L/W2NgYv/jFL2Lv3r0lmBIAACCX8pEegIH27t0bDQ0NA9buuOOO2LJlS3z44Ycxbty4Ic/r6+uLvr6+/udnzpyJd999NyZMmBBlZWXDOjMAAHzaFUURJ06ciMmTJ8eYMW6MLTVhO8p0dXVFTU3NgLWampo4depUdHd3x6RJk4Y8r6mpKTZs2FCKEQEAgHM4cuRITJkyZaTH+NQRtqPQH15hPXu3+MddeV23bl2sWbOm/3lPT09cf/31ceTIkaiqqhqeQQEAgIiI6O3tjalTp8af/umfjvQon0rCdpS59tpro6ura8DasWPHory8PCZMmHDO8yoqKqKiomLQelVVlbAFAIAS8THAkeHm71Fm/vz50draOmBt+/btMWfOnHN+vhYAAODTTNgOs/feey/2798f+/fvj4iPvs5n//790dHREREf3UK8fPny/uMbGxvj7bffjjVr1sTBgwdj69atsWXLlnjggQdGYnwAAIBRz63Iw2zfvn1xyy239D8/+znYe+65J5599tno7Ozsj9yIiOnTp0dLS0usXr06nnzyyZg8eXI8/vjj8fWvf73kswMAAGTge2wvU729vVFdXR09PT0+YwsAAMPM+++R5VZkAAAAUhO2AAAApCZsAQAASE3YAgAAkJqwBQAAIDVhCwAAQGrCFgAAgNSELQAAAKkJWwAAAFITtgAAAKQmbAEAAEhN2AIAAJCasAUAACA1YQsAAEBqwhYAAIDUhC0AAACpCVsAAABSE7YAAACkJmwBAABITdgCAACQmrAFAAAgNWELAABAasIWAACA1IQtAAAAqQlbAAAAUhO2AAAApCZsAQAASE3YAgAAkJqwBQAAIDVhCwAAQGrCFgAAgNSELQAAAKkJWwAAAFITtgAAAKQmbAEAAEhN2AIAAJCasAUAACA1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338/Zs2aFU888cR5He/9d4kVpDR37tyisbFxwNrNN99crF27dsjj/+Ef/qG4+eabB6x985vfLL7yla8M24zkcKF7aShf/OIXiw0bNlzq0UjmYvfSsmXLin/8x38sHnnkkWLWrFnDOCFZXOhe+rd/+7eiurq6OH78eCnGI5kL3U///M//XNxwww0D1h5//PFiypQpwzYj+URE8corr3zsMd5/l5YrtgmdPHky2traoqGhYcB6Q0ND7NmzZ8hz9u7dO+j4O+64I/bt2xcffvjhsM3K6HYxe+kPnTlzJk6cOBFXX331cIxIEhe7l5555pl466234pFHHhnuEUniYvbSa6+9FnPmzInvf//7cd1118VNN90UDzzwQPz+978vxciMYhezn+rq6uLo0aPR0tISRVHEO++8Ey+++GLceeedpRiZy4j336VVPtIDcOG6u7vj9OnTUVNTM2C9pqYmurq6hjynq6tryONPnToV3d3dMWnSpGGbl9HrYvbSH/rBD34Q77//fixdunQ4RiSJi9lLv/71r2Pt2rWxa9euKC/3f0d85GL20qFDh2L37t1RWVkZr7zySnR3d8e3vvWtePfdd33O9lPuYvZTXV1dbNu2LZYtWxb/+7//G6dOnYq/+qu/ih/+8IelGJnLiPffpeWKbWJlZWUDnhdFMWjtjx0/1DqfPhe6l856/vnn43vf+140NzfHNddcM1zjkcj57qXTp0/HXXfdFRs2bIibbrqpVOORyIX8Xjpz5kyUlZXFtm3bYu7cubFo0aJ47LHH4tlnn3XVloi4sP104MCBWLlyZTz88MPR1tYWr7/+ehw+fDgaGxtLMSqXGe+/S8dfkSc0ceLEGDt27KC/aTx27NigvxU669prrx3y+PLy8pgwYcKwzcrodjF76azm5uZYsWJFvPDCC3HbbbcN55gkcKF76cSJE7Fv375ob2+P73znOxHxUZwURRHl5eWxffv2uPXWW0syO6PLxfxemjRpUlx33XVRXV3dvzZjxowoiiKOHj0aN95447DOzOh1MfupqakpFixYEA8++GBERHzpS1+Kq666Kurr6+PRRx91lY3z5v13ablim9D48eOjtrY2WltbB6y3trZGXV3dkOfMnz9/0PHbt2+POXPmxLhx44ZtVka3i9lLER9dqb333nvjueee85kjIuLC91JVVVX88pe/jP379/c/Ghsb4wtf+ELs378/5s2bV6rRGWUu5vfSggUL4re//W289957/Wu/+tWvYsyYMTFlypRhnZfR7WL20wcffBBjxgx8izx27NiI+P9X2+B8eP9dYiP0L63iE/rpT39ajBs3rtiyZUtx4MCBYtWqVcVVV11V/M///E9RFEWxdu3a4u677+4//tChQ8WVV15ZrF69ujhw4ECxZcuWYty4ccWLL744Ui+BUeJC99Jzzz1XlJeXF08++WTR2dnZ//jd7343Ui+BUeJC99If8m9F5qwL3UsnTpwopkyZUvz1X/918eabbxY7duwobrzxxuL+++8fqZfAKHKh++mZZ54pysvLi02bNhVvvfVWsXv37mLOnDnF3LlzR+olMEqcOHGiaG9vL9rb24uIKB577LGivb29ePvtt4ui8P57pAnbxJ588sli2rRpxfjx44vZs2cXO3bs6P/P7rnnnuKrX/3qgOP/8z//s/iLv/iLYvz48cXnPve5YvPmzSWemNHqQvbSV7/61SIiBj3uueee0g/OqHOhv5f+L2HL/3Whe+ngwYPFbbfdVlxxxRXFlClTijVr1hQffPBBiadmtLrQ/fT4448XX/ziF4srrriimDRpUvE3f/M3xdGjR0s8NaPNf/zHf3zseyDvv0dWWVG4pwIAAIC8fMYWAACA1IQtAAAAqQlbAAAAUhO2AAAApCZsAQAASE3YAgAAkJqwBQAAIDVhCwAAQGrCFgAAgNSELQAAAKkJWwAAAFITtgAAAKQmbAEAAEhN2AIAAJCasAUAACA1YQsAAEBqwhYAAIDUhC0AAACpCVsAAABSE7YAAACkJmwBAABITdgCAACQmrAFAAAgNWELAABAasIWAACA1IQtAAAAqQlbAAAAUhO2AAAApCZsAQAASE3YAgAAkJqwBQAAIDVhCwAAQGrCFgAAgNSELQAAAKkJWwAAAFITtgAAAKQmbAEAAEhN2AIAAJCasAUAACA1YQsAAEBqwhYAAIDUhC0AAACpCVsAAABSE7YAAACkJmyH2c6dO2Px4sUxefLkKCsri1dfffWPnrNjx46ora2NysrKuOGGG+Kpp54a/kEBAACSErbD7P33349Zs2bFE088cV7HHz58OBYtWhT19fXR3t4eDz30UKxcuTJeeumlYZ4UAAAgp7KiKIqRHuLToqysLF555ZVYsmTJOY/57ne/G6+99locPHiwf62xsTF+8YtfxN69e0swJQAAQC7lIz0AA+3duzcaGhoGrN1xxx2xZcuW+PDDD2PcuHFDntfX1xd9fX39z8+cORPvvvtuTJgwIcrKyoZ1ZgAA+LQriiJOnDgRkydPjjFj3BhbasJ2lOnq6oqampoBazU1NXHq1Kno7u6OSZMmDXleU1NTbNiwoRQjAgAA53DkyJGYMmXKSI/xqSNsR6E/vMJ69m7xj7vyum7dulizZk3/856enrj++uvjyJEjUVVVNTyDAgAAERHR29sbU6dOjT/90z8d6VE+lYTtKHPttddGV1fXgLVjx45FeXl5TJgw4ZznVVRUREVFxaD1qqoqYQsAACXiY4Ajw83fo8z8+fOjtbV1wNr27dtjzpw55/x8LQAAwKeZsB1m7733Xuzfvz/2798fER99nc/+/fujo6MjIj66hXj58uX9xzc2Nsbbb78da9asiYMHD8bWrVtjy5Yt8cADD4zE+AAAAKOeW5GH2b59++KWW27pf372c7D33HNPPPvss9HZ2dkfuRER06dPj5aWlli9enU8+eSTMXny5Hj88cfj61//eslnBwAAyMD32F6ment7o7q6Onp6enzGFgAAhpn33yPLrcgAAACkJmwBAABITdgCAACQmrAFAAAgNWELAABAasIWAACA1IQtAAAAqQlbAAAAUhO2AAAApCZsAQAASE3YAgAAkJqwBQAAIDVhCwAAQGrCFgAAgNSELQAAAKkJWwAAAFITtgAAAKQmbAEAAEhN2AIAAJCasAUAACA1YQsAAEBqwhYAAIDUhC0AAACpCVsAAABSE7YAAACkJmwBAABITdgCAACQmrAFAAAgNWELAABAasIWAACA1IQtAAAAqQlbAAAAUhO2AAAApCZsAQAASE3YAgAAkJqwBQAAIDVhCwAAQGrCFgAAgNSELQAAAKkJWwAAAFITtgAAAKQmbAEAAEhN2AIAAJCasAUAACA1YQsAAEBqwhYAAIDUhC0AAACpCVsAAABSE7YAAACkJmwBAABITdgCAACQmrAFAAAgNWELAABAasIWAACA1IQtAAAAqQlbAAAAUhO2AAAApCZsAQAASE3YAgAAkJqwLZFNmzbF9OnTo7KyMmpra2PXrl0fe/y2bdti1qxZceWVV8akSZPivvvui+PHj5doWgAAgDyEbQk0NzfHqlWrYv369dHe3h719fWxcOHC6OjoGPL43bt3x/Lly2PFihXx5ptvxgsvvBA///nP4/777y/x5AAAAKOfsC2Bxx57LFasWBH3339/zJgxI/7lX/4lpk6dGps3bx7y+P/6r/+Kz33uc7Fy5cqYPn16/OVf/mV885vfjH379pV4cgAAgNFP2A6zkydPRltbWzQ0NAxYb2hoiD179gx5Tl1dXRw9ejRaWlqiKIp455134sUXX4w777zznD+nr68vent7BzwAAAA+DYTtMOvu7o7Tp09HTU3NgPWampro6uoa8py6urrYtm1bLFu2LMaPHx/XXnttfOYzn4kf/vCH5/w5TU1NUV1d3f+YOnXqJX0dAAAAo5WwLZGysrIBz4uiGLR21oEDB2LlypXx8MMPR1tbW7z++utx+PDhaGxsPOefv27duujp6el/HDly5JLODwAAMFqVj/QAl7uJEyfG2LFjB12dPXbs2KCruGc1NTXFggUL4sEHH4yIiC996Utx1VVXRX19fTz66KMxadKkQedUVFRERUXFpX8BAAAAo5wrtsNs/PjxUVtbG62trQPWW1tbo66ubshzPvjggxgzZuD/NGPHjo2Ij670AgAA8P8J2xJYs2ZNPP3007F169Y4ePBgrF69Ojo6OvpvLV63bl0sX768//jFixfHyy+/HJs3b45Dhw7FG2+8EStXroy5c+fG5MmTR+plAAAAjEpuRS6BZcuWxfHjx2Pjxo3R2dkZM2fOjJaWlpg2bVpERHR2dg74Ttt77703Tpw4EU888UT8/d//fXzmM5+JW2+9Nf7pn/5ppF4CAADAqFVWuLf1stTb2xvV1dXR09MTVVVVIz0OAABc1rz/HlluRQYAACA1YQsAAEBqwhYAAIDUhC0AAACpCVsAAABSE7YAAACkJmwBAABITdgCAACQmrAFAAAgNWELAABAasIWAACA1IQtAAAAqQlbAAAAUhO2AAAApCZsAQAASE3YAgAAkJqwBQAAIDVhCwAAQGrCFgAAgNSELQAAAKkJWwAAAFITtgAAAKQmbAEAAEhN2AIAAJCasAUAACA1YQsAAEBqwhYAAIDUhC0AAACpCVsAAABSE7YAAACkJmwBAABITdgCAACQmrAFAAAgNWELAABAasIWAACA1IQtAAAAqQlbAAAAUhO2AAAApCZsAQAASE3YAgAAkJqwBQAAIDVhCwAAQGrCFgAAgNSELQAAAKkJWwAAAFITtgAAAKQmbAEAAEhN2AIAAJCasAUAACA1YQsAAEBqwhYAAIDUhC0AAACpCVsAAABSE7YAAACkJmwBAABITdgCAACQmrAFAAAgNWELAABAasIWAACA1IRtiWzatCmmT58elZWVUVtbG7t27frY4/v6+mL9+vUxbdq0qKioiM9//vOxdevWEk0LAACQR/lID/Bp0NzcHKtWrYpNmzbFggUL4kc/+lEsXLgwDhw4ENdff/2Q5yxdujTeeeed2LJlS/zZn/1ZHDt2LE6dOlXiyQEAAEa/sqIoipEe4nI3b968mD17dmzevLl/bcaMGbFkyZJoamoadPzrr78e3/jGN+LQoUNx9dVXX9TP7O3tjerq6ujp6YmqqqqLnh0AAPjjvP8eWW5FHmYnT56Mtra2aGhoGLDe0NAQe/bsGfKc1157LebMmRPf//7347rrroubbropHnjggfj9739/zp/T19cXvb29Ax4AAACfBm5FHmbd3d1x+vTpqKmpGbBeU1MTXV1dQ55z6NCh2L17d1RWVsYrr7wS3d3d8a1vfSvefffdc37OtqmpKTZs2HDJ5wcAABjtXLEtkbKysgHPi6IYtHbWmTNnoqysLLZt2xZz586NRYsWxWOPPRbPPvvsOa/arlu3Lnp6evofR44cueSvAQAAYDRyxXaYTZw4McaOHTvo6uyxY8cGXcU9a9KkSXHddddFdXV1/9qMGTOiKIo4evRo3HjjjYPOqaioiIqKiks7PAAAQAKu2A6z8ePHR21tbbS2tg5Yb21tjbq6uiHPWbBgQfz2t7+N9957r3/tV7/6VYwZMyamTJkyrPMCAABkI2xLYM2aNfH000/H1q1b4+DBg7F69ero6OiIxsbGiPjoNuLly5f3H3/XXXfFhAkT4r777osDBw7Ezp0748EHH4y//du/jSuuuGKkXgYAAMCo5FbkEli2bFkcP348Nm7cGJ2dnTFz5sxoaWmJadOmRUREZ2dndHR09B//J3/yJ9Ha2hp/93d/F3PmzIkJEybE0qVL49FHHx2plwAAADBq+R7by5Tv0QIAgNLx/ntkuRUZAACA1IQtAAAAqQlbAAAAUhO2AAAApCZsAQAASE3YAgAAkJqwBQAAIDVhCwAAQGrCFgAAgNSELQAAAKkJWwAAAFITtgAAAKQmbAEAAEhN2AIAAJCasAUAACA1YQsAAEBqwhYAAIDUhC0AAACpCVsAAABSE7YAAACkJmwBAABITdgCAACQmrAFAAAgNWELAABAasIWAACA1IQtAAAAqQlbAAAAUhO2AAAApCZsAQAASE3YAgAAkJqwBQAAIDVhCwAAQGrCFgAAgNSELQAAAKkJWwAAAFITtgAAAKQmbAEAAEhN2AIAAJCasAUAACA1YQsAAEBqwhYAAIDUhC0AAACpCVsAAABSE7YAAACkJmwBAABITdgCAACQmrAFAAAgNWELAABAasIWAACA1IQtAAAAqQlbAAAAUhO2AAAApCZsAQAASE3YAgAAkJqwBQAAIDVhCwAAQGrCFgAAgNSELQAAAKkJWwAAAFITtiWyadOmmD59elRWVkZtbW3s2rXrvM574403ory8PL785S8P74AAAABJCdsSaG5ujlWrVsX69eujvb096uvrY+HChdHR0fGx5/X09MTy5cvja1/7WokmBQAAyKesKIpipIe43M2bNy9mz54dmzdv7l+bMWNGLFmyJJqams553je+8Y248cYbY+zYsfHqq6/G/v37z/tn9vb2RnV1dfT09ERVVdUnGR8AAPgjvP8eWa7YDrOTJ09GW1tbNDQ0DFhvaGiIPXv2nPO8Z555Jt5666145JFHzuvn9PX1RW9v74AHAADAp4GwHWbd3d1x+vTpqKmpGbBeU1MTXV1dQ57z61//OtauXRvbtm2L8vLy8/o5TU1NUV1d3f+YOnXqJ54dAAAgA2FbImVlZQOeF0UxaC0i4vTp03HXXXfFhg0b4qabbjrvP3/dunXR09PT/zhy5MgnnhkAACCD87scyEWbOHFijB07dtDV2WPHjg26ihsRceLEidi3b1+0t7fHd77znYiIOHPmTBRFEeXl5bF9+/a49dZbB51XUVERFRUVw/MiAAAARjFXbIfZ+PHjo7a2NlpbWwest7a2Rl1d3aDjq6qq4pe//GXs37+//9HY2Bhf+MIXYv/+/TFv3rxSjQ4AAJCCK7YlsGbNmrj77rtjzpw5MX/+/Pjxj38cHR0d0djYGBEf3Ub8m9/8Jn7yk5/EmDFjYubMmQPOv+aaa6KysnLQOgAAAMK2JJYtWxbHjx+PjRs3RmdnZ8ycOTNaWlpi2rRpERHR2dn5R7/TFgAAgKH5HtvLlO/RAgCA0vH+e2T5jC0AAACpCVsAAABSE7YAAACkJmwBAABITdgCAACQmrAFAAAgNWELAABAasIWAACA1IQtAAAAqQlbAAAAUhO2AAAApCZsAQAASE3YAgAAkJqwBQAAIDVhCwAAQGrCFgAAgNSELQAAAKkJWwAAAFITtgAAAKQmbAEAAEhN2AIAAJCasAUAACA1YQsAAEBqwhYAAIDUhC0AAACpCVsAAABSE7YAAACkJmwBAABITdgCAACQmrAFAAAgNWELAABAasIWAACA1IQtAAAAqQlbAAAAUhO2AAAApCZsAQAASE3YAgAAkJqwBQAAIDVhCwAAQGrCFgAAgNSELQAAAKkJWwAAAFITtgAAAKQmbAEAAEhN2AIAAJCasAUAACA1YQsAAEBqwhYAAIDUhC0AAACpCVsAAABSE7YAAACkJmwBAABITdgCAACQmrAFAAAgNWELAABAasIWAACA1IQtAAAAqQlbAAAAUhO2AAAApCZsS2TTpk0xffr0qKysjNra2ti1a9c5j3355Zfj9ttvj89+9rNRVVUV8+fPj5/97GclnBYAACAPYVsCzc3NsWrVqli/fn20t7dHfX19LFy4MDo6OoY8fufOnXH77bdHS0tLtLW1xS233BKLFy+O9vb2Ek8OAAAw+pUVRVGM9BCXu3nz5sXs2bNj8+bN/WszZsyIJUuWRFNT03n9GX/+538ey5Yti4cffvi8ju/t7Y3q6uro6emJqqqqi5obAAA4P95/jyxXbIfZyZMno62tLRoaGgasNzQ0xJ49e87rzzhz5kycOHEirr766nMe09fXF729vQMeAAAAnwbCdph1d3fH6dOno6amZsB6TU1NdHV1ndef8YMf/CDef//9WLp06TmPaWpqiurq6v7H1KlTP9HcAAAAWQjbEikrKxvwvCiKQWtDef755+N73/teNDc3xzXXXHPO49atWxc9PT39jyNHjnzimQEAADIoH+kBLncTJ06MsWPHDro6e+zYsUFXcf9Qc3NzrFixIl544YW47bbbPvbYioqKqKio+MTzAgAAZOOK7TAbP3581NbWRmtr64D11tbWqKurO+d5zz//fNx7773x3HPPxZ133jncYwIAAKTlim0JrFmzJu6+++6YM2dOzJ8/P3784x9HR0dHNDY2RsRHtxH/5je/iZ/85CcR8VHULl++PP71X/81vvKVr/Rf7b3iiiuiurp6xF4HAADAaCRsS2DZsmVx/Pjx2LhxY3R2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7WL20h86c+ZMnDhxIq6++urhGJEkLnYvPfPMM/HWW2/FI488MtwjksTF7KXXXnst5syZE9///vfjuuuui5tuuikeeOCB+P3vf1+KkRnFLmY/1dXVxdGjR6OlpSWKooh33nknXnzxxbjzzjtLMTKXEe+/S6t8pAfgwnV3d8fp06ejpqZmwHpNTU10dXUNeU5XV9eQx586dSq6u7tj0qRJwzYvo9fF7KU/9IMf/CDef//9WLp06XCMSBIXs5d+/etfx9q1a2PXrl1RXu7/jvjIxeylQ4cOxe7du6OysjJeeeWV6O7ujm9961vx7rvv+pztp9zF7Ke6urrYtm1bLFu2LP73f/83Tp06FX/1V38VP/zhD0sxMpcR779LyxXbxMrKygY8L4pi0NofO36odT59LnQvnfX888/H9773vWhubo5rrrlmuMYjkfPdS6dPn4677rorNmzYEDfddFOpxiORC/m9dObMmSgrK4tt27bF3LlzY9GiRfHYY4/Fs88+66otEXFh++nAgQOxcuXKePjhh6OtrS1ef/31OHz4cDQ2NpZiVC4z3n+Xjr8iT2jixIkxduzYQX/TeOzYsUF/K3TWtddeO+Tx5eXlMWHChGGbldHtYvbSWc3NzbFixYp44YUX4rbbbhvOMUngQvfSiRMnYt++fdHe3h7f+c53IuKjOCmKIsrLy2P79u1x6623lmR2RpeL+b00adKkuO6666K6urp/bcaMGVEURRw9ejRuvPHGYZ2Z0eti9lNTU1MsWLAgHnzwwYiI+NKXvhRXXXVV1NfXx6OPPuoqG+fN++/ScsU2ofHjx0dtbW20trYOWG9tbY26urohz5k/f/6g47dv3x5z5syJcePGDdusjG4Xs5ciPrpSe++998Zzzz3nM0dExIXvpaqqqvjlL38Z+/fv7380NjbGF77whdi/f3/MmzevVKMzylzM76UFCxbEb3/723jvvff61371q1/FmDFjYsqUKcM6L6PbxeynDz74IMaMGfgWeezYsRHx/6+2wfnw/rvERuhfWsUn9NOf/rQYN25csWXLluLAgQPFqlWriquuuqr4n//5n6IoimLt2rXF3Xff3X/8oUOHiiuvvLJYvXp1ceDAgWLLli3FuHHjihdffHGkXgKjxIXupeeee64oLy8vnnzyyaKzs7P/8bvf/W6kXgKjxIXupT/k34rMWRe6l06cOFFMmTKl+Ou//uvizTffLHbs2FHceOONxf333z9SL4FR5EL30zPPPFOUl5cXmzZtKt56661i9+7dxZw5c4q5c+eO1EtglDhx4kTR3t5etLe3FxFRPPbYY0V7e3vx9ttvF0Xh/fdIE7aJPfnkk8W0adOK8ePHF7Nnzy527NjR/5/dc889xVe/+tUBx//nf/5n8Rd/8RfF+PHji8997nPF5s2bSzwxo9WF7KWvfvWrRUQMetxzzz2lH5xR50J/L/1fwpb/60L30sGDB4vbbrutuOKKK4opU6YUa9asKT744IMST81odaH76fHHHy+++MUvFldccUUxadKk4m/+5m+Ko0ePlnhqRpv/+I//+Nj3QN5/j6yyonBPBQAAAHn5jC0AAACpCVsAAABSE7YAAACkJmwBAABITdgCAACQmrAFAAAgNWELAABAasIWAACA1IQtAAAAqQlbAAAAUhO2AAAApCZsAQAASE3YAgAAkJqwBQAAIDVhCwAAQGrCFgAAgNSELQAAAKkJWwAAAFITtgAAAKQmbAEAAEhN2AIAAJCasAUAACA1YQsAAEBqwhYAAIDUhC0AAACpCVsAAABSE7YAAACkJmwBAABITdgCAACQmrAFAAAgNWELAABAasIWAACA1IQtAAAAqQlbAAAAUhO2AAAApCZsAQAASE3YAgAAkJqwBQAAIDVhCwAAQGrCFgAAgNSELQAAAKkJWwAAAFITtgAAAKQmbAEAAEhN2AIAAJCasAUAACA1YQsAAEBqwhYAAIDUhC0AAACpCVsAAABSE7YAAACkJmwBAABITdgCAACQmrAFAAAgNWELAABAasIWAACA1IQtAAAAqQlbAAAAUhO2AAAApCZsAQAASE3YAgAAkJqwBQAAIDVhCwAAQGrCFgAAgNSELQAAAKkJWwAAAFITtgAAAKQmbAEAAEhN2AIAAJCasAUAACA1YQsAAEBqwhYAAIDUhC0AAACpCVsAAABSE7YAAACkJmwBAABITdgCAACQmrAFAAAgNWELAABAasIWAACA1IQtAAAAqQlbAAAAUhO2AAAApCZsAQAASE3YAgAAkJqwBQAAIDVhCwAAQGrCFgAAgNT+H/olNl1oxjotAAAAAElFTkSuQmCC", - "text/html": [ - "\n", - "
\n", - "
\n", - " Figure\n", - "
\n", - " \n", - "
\n", - " " - ], - "text/plain": [ - "Canvas(toolbar=Toolbar(toolitems=[('Home', 'Reset original view', 'home', 'home'), ('Back', 'Back to previous …" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# QC Plot\n", - "if ('qc_'+qc_variable) in ds.variables:\n", - "\n", - " # Plot\n", - " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", - " qc_display.add_subplots((2,), figsize = (9.5,10))\n", - " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", - " qc_ax.grid()\n", - " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", - "\n", - " plt.show()\n", - "else:\n", - " print(f'QC not available for the selected field: {qc_variable}')\n" - ] - }, - { - "cell_type": "markdown", - "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", - "metadata": {}, - "source": [ - "## Field selection dropdown menu\n", - "Select variable to be plotted from a dropdown menu" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", - "metadata": {}, - "outputs": [], - "source": [ - "plt.ioff()\n", - "\n", - "# populate dropdown menu with available variables \n", - "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", - "d_variable = 'pressure'\n", - "dropdown = widgets.Dropdown(\n", - " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", - " value= d_variable,\n", - " description='Field:',\n", - " disabled=False,\n", - ")\n", - "dropdown.layout.margin = '0px 30% 0px 20%'\n", - "dropdown.layout.width = '50%'\n", - "\n", - "# set up display\n", - "i_display = act.plotting.TimeSeriesDisplay(ds)\n", - "i_display.add_subplots((1,), figsize = (9.5,5))\n", - "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", - "i_ax.grid()\n", - "i_fig = i_display.fig\n", - "\n", - "# update plot callback function\n", - "def update_plot(change):\n", - " i_ax.cla()\n", - " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", - " i_ax_new.grid()\n", - " i_fig.canvas.draw()\n", - " i_fig.canvas.flush_events()\n", - "\n", - "dropdown.observe(update_plot, names='value')\n", - "\n", - "widgets.AppLayout(\n", - " header=dropdown,\n", - " center=i_fig.canvas,\n", - " pane_heights=[1, 6,1]\n", - ")\n" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.16" - }, - "widgets": { - "application/vnd.jupyter.widget-state+json": { - "state": {}, - "version_major": 2, - "version_minor": 0 - } - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/VAPs/quicklook/CCNPROF/.ipynb_checkpoints/rlccnprof1ghan.c1-checkpoint.ipynb b/VAPs/quicklook/CCNPROF/.ipynb_checkpoints/rlccnprof1ghan.c1-checkpoint.ipynb deleted file mode 100644 index 696f23b7..00000000 --- a/VAPs/quicklook/CCNPROF/.ipynb_checkpoints/rlccnprof1ghan.c1-checkpoint.ipynb +++ /dev/null @@ -1,4109 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "70840257-70e4-45e2-b491-14bff5a257a3", - "metadata": {}, - "source": [ - "# RLCCNPROF1GHAN.C1 Plots\n", - "\n", - "[Click here](https://www.arm.gov/capabilities/vaps/ccnprof) for more information about this vap." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "460fd89f-e034-452c-b837-f65c5958264f", - "metadata": {}, - "outputs": [], - "source": [ - "%matplotlib widget\n", - "import ipywidgets as widgets\n", - "\n", - "import matplotlib.pyplot as plt\n", - "import ipywidgets as widgets\n", - "import numpy as np\n", - "import pandas as pd\n", - "import os\n", - "from datetime import datetime\n", - "\n", - "import act\n", - "import xarray as xr\n", - "\n", - "# Data archive directory\n", - "DATA_DIR = r'/data/archive/'\n", - "\n", - "# Datastream info\n", - "DATASTREAM_NAME = 'rlccnprof1ghan'\n", - "DATA_LEVEL = 'c1'\n", - "LOCATIONS = [{'end_date': '2014-06-24', 'facility': 'C1', 'site': 'sgp', 'start_date': '2006-09-15'}]" - ] - }, - { - "cell_type": "markdown", - "id": "9faaf875", - "metadata": {}, - "source": [ - "## Define site, facility, and date range" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "ac6764f5", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "The following locations and date ranges are available for this VAP:\n" - ] - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
sitefacilitystart_dateend_date
0sgpC12006-09-152014-06-24
\n", - "
" - ], - "text/plain": [ - " site facility start_date end_date\n", - "0 sgp C1 2006-09-15 2014-06-24" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "print(\"The following locations and date ranges are available for this VAP:\")\n", - "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" - ] - }, - { - "cell_type": "markdown", - "id": "8d132223", - "metadata": {}, - "source": [ - "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "e563983a", - "metadata": {}, - "outputs": [], - "source": [ - "site_facility = ( 'sgp', 'C1' )\n", - "\n", - "date_start = '2014-06-22'\n", - "date_end = '2014-06-24'" - ] - }, - { - "cell_type": "markdown", - "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", - "metadata": {}, - "source": [ - "## Load data files\n", - "Load data files from /data/archive/" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'/data/archive/sgp/sgprlccnprof1ghanC1.c1'" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Compile list of files\n", - "site, facility = site_facility\n", - "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", - "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", - "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", - "dir_path\n" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "6be8f3dc", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "['20140622', '20140623', '20140624']" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "from datetime import date, timedelta\n", - "import pandas as pd\n", - "\n", - "def get_ARM_formated_dates(start_date, end_date):\n", - " \"\"\"\n", - " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", - " EXAMPLE:\n", - " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", - " >> [\"20180219\", \"20180220\", \"20180221\"] \n", - " \"\"\"\n", - " \n", - " _start_date = pd.to_datetime(start_date)\n", - " _end_date = pd.to_datetime(end_date)\n", - " \n", - " delta = _end_date - _start_date # returns timedelta \n", - " dates = []\n", - "\n", - " for i in range(delta.days + 1):\n", - " day = _start_date + timedelta(days=i)\n", - " day_formated = day.strftime(format=\"%Y%m%d\")\n", - " dates.append(day_formated)\n", - " return dates\n", - "\n", - "\n", - "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "51feea2e", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "['/data/archive/sgp/sgprlccnprof1ghanC1.c1/sgprlccnprof1ghanC1.c1.20140624.000000.cdf']" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Filter a list of files based on date pattern\n", - "import glob\n", - "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", - "files_filter = []\n", - "for date in dates:\n", - " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", - " files_filter\n", - "files_filter" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "id": "b0e5d0d7", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "1 files loaded\n" - ] - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "
<xarray.Dataset>\n",
-       "Dimensions:                           (time: 24, height: 52, ss_step: 7,\n",
-       "                                       param2: 2)\n",
-       "Coordinates:\n",
-       "  * time                              (time) datetime64[ns] 2014-06-24 ... 20...\n",
-       "  * height                            (height) float32 0.15 0.225 ... 3.9 3.975\n",
-       "  * ss_step                           (ss_step) float32 1.0 2.0 3.0 ... 6.0 7.0\n",
-       "Dimensions without coordinates: param2\n",
-       "Data variables: (12/62)\n",
-       "    base_time                         datetime64[ns] 2014-06-24\n",
-       "    time_offset                       (time) datetime64[ns] 2014-06-24 ... 20...\n",
-       "    qc_time                           (time) int32 dask.array<chunksize=(24,), meta=np.ndarray>\n",
-       "    rh_mean                           (time, height) float32 dask.array<chunksize=(24, 52), meta=np.ndarray>\n",
-       "    qc_rh_mean                        (time, height) int32 dask.array<chunksize=(24, 52), meta=np.ndarray>\n",
-       "    rh_std_dev                        (time, height) float32 dask.array<chunksize=(24, 52), meta=np.ndarray>\n",
-       "    ...                                ...\n",
-       "    qc_N_CCN_7                        (time) int32 dask.array<chunksize=(24,), meta=np.ndarray>\n",
-       "    temperature_second_deriv          (time, height) float32 dask.array<chunksize=(24, 52), meta=np.ndarray>\n",
-       "    cbh                               (time) float32 dask.array<chunksize=(24,), meta=np.ndarray>\n",
-       "    lat                               float32 ...\n",
-       "    lon                               float32 ...\n",
-       "    alt                               float32 ...\n",
-       "Attributes: (12/17)\n",
-       "    command_line:                   ccnprof -s sgp -f C1 -b 20140624 -e 20140...\n",
-       "    process_version:                v1.2\n",
-       "    dod_version:                    rlccnprof1ghan-c1-0.5\n",
-       "    site_id:                        sgp\n",
-       "    facility_id:                    C1: Lamont, Oklahoma\n",
-       "    input_datastreams:              sgpaosccn100C1.a1 : 12.9 : 20140624.00000...\n",
-       "    ...                             ...\n",
-       "    history:                        created by user dsmgr on machine iron at ...\n",
-       "    _file_dates:                    ['20140624']\n",
-       "    _file_times:                    ['000000']\n",
-       "    datastream:                     \n",
-       "    _datastream:                    \n",
-       "    _arm_standards_flag:            1
" - ], - "text/plain": [ - "\n", - "Dimensions: (time: 24, height: 52, ss_step: 7,\n", - " param2: 2)\n", - "Coordinates:\n", - " * time (time) datetime64[ns] 2014-06-24 ... 20...\n", - " * height (height) float32 0.15 0.225 ... 3.9 3.975\n", - " * ss_step (ss_step) float32 1.0 2.0 3.0 ... 6.0 7.0\n", - "Dimensions without coordinates: param2\n", - "Data variables: (12/62)\n", - " base_time datetime64[ns] 2014-06-24\n", - " time_offset (time) datetime64[ns] 2014-06-24 ... 20...\n", - " qc_time (time) int32 dask.array\n", - " rh_mean (time, height) float32 dask.array\n", - " qc_rh_mean (time, height) int32 dask.array\n", - " rh_std_dev (time, height) float32 dask.array\n", - " ... ...\n", - " qc_N_CCN_7 (time) int32 dask.array\n", - " temperature_second_deriv (time, height) float32 dask.array\n", - " cbh (time) float32 dask.array\n", - " lat float32 ...\n", - " lon float32 ...\n", - " alt float32 ...\n", - "Attributes: (12/17)\n", - " command_line: ccnprof -s sgp -f C1 -b 20140624 -e 20140...\n", - " process_version: v1.2\n", - " dod_version: rlccnprof1ghan-c1-0.5\n", - " site_id: sgp\n", - " facility_id: C1: Lamont, Oklahoma\n", - " input_datastreams: sgpaosccn100C1.a1 : 12.9 : 20140624.00000...\n", - " ... ...\n", - " history: created by user dsmgr on machine iron at ...\n", - " _file_dates: ['20140624']\n", - " _file_times: ['000000']\n", - " datastream: \n", - " _datastream: \n", - " _arm_standards_flag: 1" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Load files as a single dataset\n", - "files_list = files_filter \n", - "ds = act.io.armfiles.read_netcdf(files_list)\n", - "ds.clean.cleanup()\n", - "print(f'{len(files_list)} files loaded')\n", - "ds\n" - ] - }, - { - "cell_type": "markdown", - "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", - "metadata": {}, - "source": [ - "## Plot time series data\n", - "#### Define the list of variables to be plotted:" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "id": "d09b789e-84f1-4605-846b-a72c110c8048", - "metadata": {}, - "outputs": [], - "source": [ - "variables_to_plot = ['nsteps', 'rh_mean', 'rh_std_dev']" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", - "metadata": {}, - "outputs": [ - { - "ename": "KeyError", - "evalue": "'nsteps'", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mKeyError\u001b[0m Traceback (most recent call last)", - "Cell \u001b[0;32mIn[9], line 5\u001b[0m\n\u001b[1;32m 2\u001b[0m ts_display\u001b[38;5;241m.\u001b[39madd_subplots((\u001b[38;5;28mlen\u001b[39m(variables_to_plot),), figsize \u001b[38;5;241m=\u001b[39m (\u001b[38;5;241m9.5\u001b[39m,\u001b[38;5;241m4\u001b[39m\u001b[38;5;241m*\u001b[39m\u001b[38;5;28mlen\u001b[39m(variables_to_plot)))\n\u001b[1;32m 4\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m i,v \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28menumerate\u001b[39m(variables_to_plot):\n\u001b[0;32m----> 5\u001b[0m ts_ax \u001b[38;5;241m=\u001b[39m ts_display\u001b[38;5;241m.\u001b[39mplot(v, subplot_index\u001b[38;5;241m=\u001b[39m(i,), set_title\u001b[38;5;241m=\u001b[39m\u001b[43mds\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mvariables\u001b[49m\u001b[43m[\u001b[49m\u001b[43mv\u001b[49m\u001b[43m]\u001b[49m\u001b[38;5;241m.\u001b[39mattrs[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mlong_name\u001b[39m\u001b[38;5;124m'\u001b[39m],)\n\u001b[1;32m 6\u001b[0m ts_ax\u001b[38;5;241m.\u001b[39mgrid()\n\u001b[1;32m 8\u001b[0m plt\u001b[38;5;241m.\u001b[39mshow()\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/core/utils.py:453\u001b[0m, in \u001b[0;36mFrozen.__getitem__\u001b[0;34m(self, key)\u001b[0m\n\u001b[1;32m 452\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21m__getitem__\u001b[39m(\u001b[38;5;28mself\u001b[39m, key: K) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m V:\n\u001b[0;32m--> 453\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mmapping\u001b[49m\u001b[43m[\u001b[49m\u001b[43mkey\u001b[49m\u001b[43m]\u001b[49m\n", - "\u001b[0;31mKeyError\u001b[0m: 'nsteps'" - ] - }, - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "b9705370d4b8484a87f6a3e53aec927d", - "version_major": 2, - "version_minor": 0 - }, - "image/png": "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1tnZmUVE9thjj2WdnZ3ZW2+9lWWZ6+/JJmwT9uSTT2aVlZVZYWFhNn/+/Gznzp3D/+zuu+/ObrrpphHH/+d//mf2J3/yJ1lhYWH2uc99Ltu8eXOOJ2aqGs9euummm7KIGPW4++67cz84U854f1/6XcKW3zXevXTw4MHs1ltvzS677LJs9uzZWXNzc/bBBx/keGqmqvHup8cffzz74he/mF122WVZeXl59ld/9VfZ0aNHczw1U81//Md/fOw1kOvvyZWXZd5TAQAAQLp8xhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAnbCbZr165YtmxZzJo1K/Ly8uKVV175xHN27twZ1dXVUVxcHNddd1089dRTEz8oAABAooTtBHv//fdj3rx58cQTT5zT8YcPH46lS5dGXV1ddHZ2xkMPPRSrVq2KF198cYInBQAASFNelmXZZA9xqcjLy4uXX345li9fftZjvv3tb8err74aBw8eHF5ramqKn/3sZ7Fv374cTAkAAJCWgskegJH27dsX9fX1I9Zuv/322LJlS3z44Ycxffr0Mc8bGhqKoaGh4eenT5+Od999N2bMmBF5eXkTOjMAAFzqsiyL48ePx6xZs2LaNG+MzTVhO8X09vZGWVnZiLWysrI4efJk9PX1RXl5+ZjntbS0xIYNG3IxIgAAcBZHjhyJ2bNnT/YYlxxhOwX9/h3WM+8W/7g7r+vWrYvm5ubh5wMDA3HttdfGkSNHoqSkZGIGBQAAIiJicHAwKioq4g//8A8ne5RLkrCdYq6++uro7e0dsXbs2LEoKCiIGTNmnPW8oqKiKCoqGrVeUlIibAEAIEd8DHByePP3FLNo0aJob28fsbZjx46oqak56+drAQAALmXCdoK999570dXVFV1dXRHx0df5dHV1RXd3d0R89BbixsbG4eObmprirbfeiubm5jh48GBs3bo1tmzZEg888MBkjA8AADDleSvyBNu/f3/cfPPNw8/P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7dixY1FQUBAzZsw463lFRUVRVFQ0ar2kpETYAgBAjvgY4OTw5u8pZtGiRdHe3j5ibceOHVFTU3PWz9cCAABcyoTtBHvvvfeiq6srurq6IuKjr/Pp6uqK7u7uiPjoLcSNjY3Dxzc1NcVbb70Vzc3NcfDgwdi6dWts2bIlHnjggckYHwAAYMrzVuQJtn///rj55puHn5/5HOzdd98d27Zti56enuHIjYioqqqKtra2WLNmTTz55JMxa9asePzxx+NrX/tazmcHAABIge+xvUgNDg5GaWlpDAwM+IwtAABMMNffk8tbkQEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAnbHNm0aVNUVVVFcXFxVFdXx+7duz/2+O3bt8e8efPi8ssvj/Ly8rj33nujv78/R9MCAACkQ9jmQGtra6xevTrWr18fnZ2dUVdXF0uWLInu7u4xj9+zZ080NjbGypUr44033ojnn38+fvrTn8Z9992X48kBAACmPmGbA4899lisXLky7rvvvpgzZ0780z/9U1RUVMTmzZvHPP6//uu/4nOf+1ysWrUqqqqq4k//9E/jG9/4Ruzfvz/HkwMAAEx9wnaCnThxIjo6OqK+vn7Een19fezdu3fMc2pra+Po0aPR1tYWWZbFO++8Ey+88ELccccduRgZAAAgKcJ2gvX19cWpU6eirKxsxHpZWVn09vaOeU5tbW1s3749GhoaorCwMK6++ur4zGc+E9///vfP+nOGhoZicHBwxAMAAOBSIGxzJC8vb8TzLMtGrZ1x4MCBWLVqVTz88MPR0dERr732Whw+fDiamprO+uu3tLREaWnp8KOiouKCzg8AADBV5WVZlk32EBezEydOxOWXXx7PP/98/MVf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yZlq3n///Zg3b1488cQT53S86+8cy0jSggULsqamphFrN954Y7Z27doxj/+7v/u77MYbbxyx9o1vfCP7yle+MmEzkobx7qWxfPGLX8w2bNhwoUcjMee7lxoaGrK///u/zx555JFs3rx5EzghqRjvXvq3f/u3rLS0NOvv78/FeCRmvPvpH//xH7PrrrtuxNrjjz+ezZ49e8JmJD0Rkb388ssfe4zr79xyxzZBJ06ciI6Ojqivrx+xXl9fH3v37h3znH379o06/vbbb4/9+/fHhx9+OGGzMrWdz176fadPn47jx4/HlVdeOREjkojz3UvPPPNMvPnmm/HII49M9Igk4nz20quvvho1NTXx3e9+N6655pq44YYb4oEHHojf/OY3uRiZKex89lNtbW0cPXo02traIsuyeOedd+KFF16IO+64IxcjcxFx/Z1bBZM9AOPX19cXp06dirKyshHrZWVl0dvbO+Y5vb29Yx5/8uTJ6Ovri/Ly8gmbl6nrfPbS7/ve974X77//fqxYsWIiRiQR57OXfvnLX8batWtj9+7dUVDgP0d85Hz20qFDh2LPnj1RXFwcL7/8cvT19cX9998f7777rs/ZXuLOZz/V1tbG9u3bo6GhIX7729/GyZMn48/+7M/i+9//fi5G5iLi+ju33LFNWF5e3ojnWZaNWvuk48da59Iz3r10xnPPPRff+c53orW1Na666qqJGo+EnOteOnXqVNx5552xYcOGuOGGG3I1HgkZz+9Lp0+fjry8vNi+fXssWLAgli5dGo899lhs27bNXVsiYnz76cCBA7Fq1ap4+OGHo6OjI1577bU4fPhwNDU15WJULjKuv3PHH5EnaObMmZGfnz/qTxqPHTs26k+Fzrj66qvHPL6goCBmzJgxYbMytZ3PXjqjtbU1Vq5cGc8//3zceuutEzkmCRjvXjp+/Hjs378/Ojs741vf+lZEfBQnWZZFQUFB7NixI2655ZaczM7Ucj6/L5WXl8c111wTpaWlw2tz5syJLMvi6NGjcf3110/ozExd57OfWlpaYvHixfHggw9GRMSXvvSluOKKK6Kuri4effRRd9k4Z66/c8sd2wQVFhZGdXV1tLe3j1hvb2+P2traMc9ZtGjRqON37NgRNTU1MX369AmblantfPZSxEd3au+555549tlnfeaIiBj/XiopKYmf//zn0dXVNfxoamqKL3zhC9HV1RULFy7M1ehMMefz+9LixYvj7bffjvfee2947Re/+EVMmzYtZs+ePaHzMrWdz3764IMPYtq0kZfI+fn5EfF/d9vgXLj+zrFJ+kur+JR+/OMfZ9OnT8+2bNmSHThwIFu9enV2xRVXZP/7v/+bZVmWrV27NrvrrruGjz906FB2+eWXZ2vWrMkOHDiQbdmyJZs+fXr2wgsvTNZLYIoY71569tlns4KCguzJJ5/Menp6hh+//vWvJ+slMEWMdy/9Pn8rMmeMdy8dP348mz17dvaXf/mX2RtvvJHt3Lkzu/7667P77rtvsl4CU8h499MzzzyTFRQUZJs2bcrefPPNbM+ePVlNTU22YMGCyXoJTBHHjx/POjs7s87Oziwissceeyzr7OzM3nrrrSzLXH9PNmGbsCeffDKrrKzMCgsLs/nz52c7d+4c/md33313dtNNN404/j//8z+zP/mTP8kKCwuzz33uc9nmzZtzPDFT1Xj20k033ZRFxKjH3XffnfvBmXLG+/vS7xK2/K7x7qWDBw9mt956a3bZZZdls2fPzpqbm7MPPvggx1MzVY13Pz3++OPZF7/4xeyyyy7LysvLs7/6q7/Kjh49muOpmWr+4z/+42OvgVx/T668LPOeCgAAANLlM7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQtP8PDcrXAZbhxkUAAAAASUVORK5CYII=", - "text/html": [ - "\n", - "
\n", - "
\n", - " Figure\n", - "
\n", - " \n", - "
\n", - " " - ], - "text/plain": [ - "Canvas(toolbar=Toolbar(toolitems=[('Home', 'Reset original view', 'home', 'home'), ('Back', 'Back to previous …" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", - "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", - "\n", - "for i,v in enumerate(variables_to_plot):\n", - " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", - " ts_ax.grid()\n", - "\n", - "plt.show()\n" - ] - }, - { - "cell_type": "markdown", - "id": "194399aa-1907-452b-8ba9-bc31d7f60291", - "metadata": {}, - "source": [ - "## Quality check plots\n", - "#### Define variable for QC plot" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", - "metadata": {}, - "outputs": [], - "source": [ - "qc_variable = 'rh_mean'" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", - "metadata": {}, - "outputs": [], - "source": [ - "# QC Plot\n", - "if ('qc_'+qc_variable) in ds.variables:\n", - "\n", - " # Plot\n", - " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", - " qc_display.add_subplots((2,), figsize = (9.5,10))\n", - " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", - " qc_ax.grid()\n", - " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", - "\n", - " plt.show()\n", - "else:\n", - " print(f'QC not available for the selected field: {qc_variable}')\n" - ] - }, - { - "cell_type": "markdown", - "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", - "metadata": {}, - "source": [ - "## Field selection dropdown menu\n", - "Select variable to be plotted from a dropdown menu" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", - "metadata": {}, - "outputs": [], - "source": [ - "plt.ioff()\n", - "\n", - "# populate dropdown menu with available variables \n", - "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", - "d_variable = 'nsteps'\n", - "dropdown = widgets.Dropdown(\n", - " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", - " value= d_variable,\n", - " description='Field:',\n", - " disabled=False,\n", - ")\n", - "dropdown.layout.margin = '0px 30% 0px 20%'\n", - "dropdown.layout.width = '50%'\n", - "\n", - "# set up display\n", - "i_display = act.plotting.TimeSeriesDisplay(ds)\n", - "i_display.add_subplots((1,), figsize = (9.5,5))\n", - "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", - "i_ax.grid()\n", - "i_fig = i_display.fig\n", - "\n", - "# update plot callback function\n", - "def update_plot(change):\n", - " i_ax.cla()\n", - " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", - " i_ax_new.grid()\n", - " i_fig.canvas.draw()\n", - " i_fig.canvas.flush_events()\n", - "\n", - "dropdown.observe(update_plot, names='value')\n", - "\n", - "widgets.AppLayout(\n", - " header=dropdown,\n", - " center=i_fig.canvas,\n", - " pane_heights=[1, 6,1]\n", - ")\n" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.16" - }, - "widgets": { - "application/vnd.jupyter.widget-state+json": { - "state": {}, - "version_major": 2, - "version_minor": 0 - } - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/VAPs/quicklook/CMAC2/.ipynb_checkpoints/cmac2.c1-checkpoint.ipynb b/VAPs/quicklook/CMAC2/.ipynb_checkpoints/cmac2.c1-checkpoint.ipynb deleted file mode 100644 index 425a9cbc..00000000 --- a/VAPs/quicklook/CMAC2/.ipynb_checkpoints/cmac2.c1-checkpoint.ipynb +++ /dev/null @@ -1,3537 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "70840257-70e4-45e2-b491-14bff5a257a3", - "metadata": {}, - "source": [ - "# CMAC2.C1 Plots\n", - "\n", - "[Click here](https://www.arm.gov/capabilities/vaps/cmac2) for more information about this vap." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "460fd89f-e034-452c-b837-f65c5958264f", - "metadata": {}, - "outputs": [], - "source": [ - "%matplotlib widget\n", - "import ipywidgets as widgets\n", - "\n", - "import matplotlib.pyplot as plt\n", - "import ipywidgets as widgets\n", - "import numpy as np\n", - "import pandas as pd\n", - "import os\n", - "from datetime import datetime\n", - "\n", - "import act\n", - "import xarray as xr\n", - "\n", - "# Data archive directory\n", - "DATA_DIR = r'/data/archive/'\n", - "\n", - "# Datastream info\n", - "DATASTREAM_NAME = 'cmac2'\n", - "DATA_LEVEL = 'c1'\n", - "LOCATIONS = [{'end_date': '2019-01-22', 'facility': 'I4', 'site': 'sgp', 'start_date': '2018-08-30'}, {'end_date': '2019-04-05', 'facility': 'I5', 'site': 'sgp', 'start_date': '2018-08-30'}, {'end_date': '2019-02-26', 'facility': 'I6', 'site': 'sgp', 'start_date': '2018-08-30'}]" - ] - }, - { - "cell_type": "markdown", - "id": "9faaf875", - "metadata": {}, - "source": [ - "## Define site, facility, and date range" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "ac6764f5", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "The following locations and date ranges are available for this VAP:\n" - ] - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
sitefacilitystart_dateend_date
0sgpI42018-08-302019-01-22
1sgpI52018-08-302019-04-05
2sgpI62018-08-302019-02-26
\n", - "
" - ], - "text/plain": [ - " site facility start_date end_date\n", - "0 sgp I4 2018-08-30 2019-01-22\n", - "1 sgp I5 2018-08-30 2019-04-05\n", - "2 sgp I6 2018-08-30 2019-02-26" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "print(\"The following locations and date ranges are available for this VAP:\")\n", - "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" - ] - }, - { - "cell_type": "markdown", - "id": "8d132223", - "metadata": {}, - "source": [ - "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "e563983a", - "metadata": {}, - "outputs": [], - "source": [ - "site_facility = ( 'sgp', 'I4' )\n", - "\n", - "date_start = '2019-01-21'\n", - "date_end = '2019-01-22'" - ] - }, - { - "cell_type": "markdown", - "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", - "metadata": {}, - "source": [ - "## Load data files\n", - "Load data files from /data/archive/" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'/data/archive/sgp/sgpcmac2I4.c1'" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Compile list of files\n", - "site, facility = site_facility\n", - "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", - "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", - "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", - "dir_path\n" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "6be8f3dc", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "['20190121', '20190122']" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "from datetime import date, timedelta\n", - "import pandas as pd\n", - "\n", - "def get_ARM_formated_dates(start_date, end_date):\n", - " \"\"\"\n", - " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", - " EXAMPLE:\n", - " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", - " >> [\"20180219\", \"20180220\", \"20180221\"] \n", - " \"\"\"\n", - " \n", - " _start_date = pd.to_datetime(start_date)\n", - " _end_date = pd.to_datetime(end_date)\n", - " \n", - " delta = _end_date - _start_date # returns timedelta \n", - " dates = []\n", - "\n", - " for i in range(delta.days + 1):\n", - " day = _start_date + timedelta(days=i)\n", - " day_formated = day.strftime(format=\"%Y%m%d\")\n", - " dates.append(day_formated)\n", - " return dates\n", - "\n", - "\n", - "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "51feea2e", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "['/data/archive/sgp/sgpcmac2I4.c1/sgpcmac2I4.c1.20190122.020009.nc',\n", - " '/data/archive/sgp/sgpcmac2I4.c1/sgpcmac2I4.c1.20190122.044928.nc',\n", - " '/data/archive/sgp/sgpcmac2I4.c1/sgpcmac2I4.c1.20190122.024236.nc',\n", - " '/data/archive/sgp/sgpcmac2I4.c1/sgpcmac2I4.c1.20190122.005559.nc',\n", - " '/data/archive/sgp/sgpcmac2I4.c1/sgpcmac2I4.c1.20190122.003457.nc',\n", - " '/data/archive/sgp/sgpcmac2I4.c1/sgpcmac2I4.c1.20190122.034552.nc',\n", - " '/data/archive/sgp/sgpcmac2I4.c1/sgpcmac2I4.c1.20190122.042813.nc',\n", - " '/data/archive/sgp/sgpcmac2I4.c1/sgpcmac2I4.c1.20190122.022111.nc',\n", - " '/data/archive/sgp/sgpcmac2I4.c1/sgpcmac2I4.c1.20190122.030339.nc',\n", - " '/data/archive/sgp/sgpcmac2I4.c1/sgpcmac2I4.c1.20190122.051031.nc',\n", - " '/data/archive/sgp/sgpcmac2I4.c1/sgpcmac2I4.c1.20190122.040656.nc',\n", - " '/data/archive/sgp/sgpcmac2I4.c1/sgpcmac2I4.c1.20190122.011706.nc',\n", - " '/data/archive/sgp/sgpcmac2I4.c1/sgpcmac2I4.c1.20190122.013821.nc',\n", - " '/data/archive/sgp/sgpcmac2I4.c1/sgpcmac2I4.c1.20190122.032444.nc',\n", - " '/data/archive/sgp/sgpcmac2I4.c1/sgpcmac2I4.c1.20190122.001346.nc']" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Filter a list of files based on date pattern\n", - "import glob\n", - "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", - "files_filter = []\n", - "for date in dates:\n", - " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", - " files_filter\n", - "files_filter" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "id": "b0e5d0d7", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "15 files loaded\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/home/kefeimo/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/act/qc/clean.py:234: RuntimeWarning: invalid value encountered in cast\n", - " data = data.astype(dtype)\n" - ] - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "
<xarray.Dataset>\n",
-       "Dimensions:                                   (time: 97200, range: 1001,\n",
-       "                                               sweep: 18)\n",
-       "Coordinates:\n",
-       "  * time                                      (time) datetime64[ns] 2019-01-2...\n",
-       "  * range                                     (range) float32 0.0 ... 1e+05\n",
-       "    azimuth                                   (time) float32 dask.array<chunksize=(6480,), meta=np.ndarray>\n",
-       "    elevation                                 (time) float32 dask.array<chunksize=(6480,), meta=np.ndarray>\n",
-       "Dimensions without coordinates: sweep\n",
-       "Data variables: (12/49)\n",
-       "    base_time                                 (time) datetime64[ns] 2019-01-2...\n",
-       "    time_offset                               (time) datetime64[ns] 2019-01-2...\n",
-       "    reflectivity                              (time, range) float32 dask.array<chunksize=(6480, 1001), meta=np.ndarray>\n",
-       "    cross_correlation_ratio_hv                (time, range) float32 dask.array<chunksize=(6480, 1001), meta=np.ndarray>\n",
-       "    normalized_coherent_power                 (time, range) float32 dask.array<chunksize=(6480, 1001), meta=np.ndarray>\n",
-       "    mean_doppler_velocity                     (time, range) float32 dask.array<chunksize=(6480, 1001), meta=np.ndarray>\n",
-       "    ...                                        ...\n",
-       "    path_integrated_attenuation               (time, range) float32 dask.array<chunksize=(6480, 1001), meta=np.ndarray>\n",
-       "    corrected_differential_reflectivity       (time, range) float32 dask.array<chunksize=(6480, 1001), meta=np.ndarray>\n",
-       "    ground_clutter                            (time, range) float32 dask.array<chunksize=(6480, 1001), meta=np.ndarray>\n",
-       "    lat                                       (time) float32 36.58 ... 36.58\n",
-       "    lon                                       (time) float32 -97.36 ... -97.36\n",
-       "    alt                                       (time) float32 330.0 ... 330.0\n",
-       "Attributes: (12/26)\n",
-       "    Conventions:           ARM-1.0 CF/Radial instrument_parameters\n",
-       "    title:                 Atmospheric Radiation Measurement (ARM) program X-...\n",
-       "    institution:           United States Department of Energy - Atmospheric R...\n",
-       "    references:            See XSAPR Instrument Handbook\n",
-       "    source:                Atmospheric Radiation Measurement (ARM) program X-...\n",
-       "    comment:               Data in this file has not be calibrated, corrected...\n",
-       "    ...                    ...\n",
-       "    original_container:    sigmet\n",
-       "    history:               created by user rjackson on machine or-condo-c215....\n",
-       "    _file_dates:           ['20190122', '20190122', '20190122', '20190122', '...\n",
-       "    _file_times:           ['001346', '003457', '005559', '011706', '013821',...\n",
-       "    _datastream:           sgpadicmac2I4.c1\n",
-       "    _arm_standards_flag:   1
" - ], - "text/plain": [ - "\n", - "Dimensions: (time: 97200, range: 1001,\n", - " sweep: 18)\n", - "Coordinates:\n", - " * time (time) datetime64[ns] 2019-01-2...\n", - " * range (range) float32 0.0 ... 1e+05\n", - " azimuth (time) float32 dask.array\n", - " elevation (time) float32 dask.array\n", - "Dimensions without coordinates: sweep\n", - "Data variables: (12/49)\n", - " base_time (time) datetime64[ns] 2019-01-2...\n", - " time_offset (time) datetime64[ns] 2019-01-2...\n", - " reflectivity (time, range) float32 dask.array\n", - " cross_correlation_ratio_hv (time, range) float32 dask.array\n", - " normalized_coherent_power (time, range) float32 dask.array\n", - " mean_doppler_velocity (time, range) float32 dask.array\n", - " ... ...\n", - " path_integrated_attenuation (time, range) float32 dask.array\n", - " corrected_differential_reflectivity (time, range) float32 dask.array\n", - " ground_clutter (time, range) float32 dask.array\n", - " lat (time) float32 36.58 ... 36.58\n", - " lon (time) float32 -97.36 ... -97.36\n", - " alt (time) float32 330.0 ... 330.0\n", - "Attributes: (12/26)\n", - " Conventions: ARM-1.0 CF/Radial instrument_parameters\n", - " title: Atmospheric Radiation Measurement (ARM) program X-...\n", - " institution: United States Department of Energy - Atmospheric R...\n", - " references: See XSAPR Instrument Handbook\n", - " source: Atmospheric Radiation Measurement (ARM) program X-...\n", - " comment: Data in this file has not be calibrated, corrected...\n", - " ... ...\n", - " original_container: sigmet\n", - " history: created by user rjackson on machine or-condo-c215....\n", - " _file_dates: ['20190122', '20190122', '20190122', '20190122', '...\n", - " _file_times: ['001346', '003457', '005559', '011706', '013821',...\n", - " _datastream: sgpadicmac2I4.c1\n", - " _arm_standards_flag: 1" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Load files as a single dataset\n", - "files_list = files_filter \n", - "ds = act.io.armfiles.read_netcdf(files_list)\n", - "ds.clean.cleanup()\n", - "print(f'{len(files_list)} files loaded')\n", - "ds\n" - ] - }, - { - "cell_type": "markdown", - "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", - "metadata": {}, - "source": [ - "## Plot time series data\n", - "#### Define the list of variables to be plotted:" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "id": "d09b789e-84f1-4605-846b-a72c110c8048", - "metadata": {}, - "outputs": [], - "source": [ - "variables_to_plot = ['reflectivity', 'cross_correlation_ratio_hv', 'normalized_coherent_power']" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", - "metadata": {}, - "outputs": [], - "source": [ - "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", - "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", - "\n", - "for i,v in enumerate(variables_to_plot):\n", - " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", - " ts_ax.grid()\n", - "\n", - "plt.show()\n" - ] - }, - { - "cell_type": "markdown", - "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", - "metadata": {}, - "source": [ - "## Field selection dropdown menu\n", - "Select variable to be plotted from a dropdown menu" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", - "metadata": {}, - "outputs": [], - "source": [ - "plt.ioff()\n", - "\n", - "# populate dropdown menu with available variables \n", - "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", - "d_variable = 'reflectivity'\n", - "dropdown = widgets.Dropdown(\n", - " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", - " value= d_variable,\n", - " description='Field:',\n", - " disabled=False,\n", - ")\n", - "dropdown.layout.margin = '0px 30% 0px 20%'\n", - "dropdown.layout.width = '50%'\n", - "\n", - "# set up display\n", - "i_display = act.plotting.TimeSeriesDisplay(ds)\n", - "i_display.add_subplots((1,), figsize = (9.5,5))\n", - "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", - "i_ax.grid()\n", - "i_fig = i_display.fig\n", - "\n", - "# update plot callback function\n", - "def update_plot(change):\n", - " i_ax.cla()\n", - " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", - " i_ax_new.grid()\n", - " i_fig.canvas.draw()\n", - " i_fig.canvas.flush_events()\n", - "\n", - "dropdown.observe(update_plot, names='value')\n", - "\n", - "widgets.AppLayout(\n", - " header=dropdown,\n", - " center=i_fig.canvas,\n", - " pane_heights=[1, 6,1]\n", - ")\n" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.16" - }, - "widgets": { - "application/vnd.jupyter.widget-state+json": { - "state": {}, - "version_major": 2, - "version_minor": 0 - } - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/VAPs/quicklook/KAZRCOR/.ipynb_checkpoints/kazrcorge.c1-checkpoint.ipynb b/VAPs/quicklook/KAZRCOR/.ipynb_checkpoints/kazrcorge.c1-checkpoint.ipynb deleted file mode 100644 index bf322ff2..00000000 --- a/VAPs/quicklook/KAZRCOR/.ipynb_checkpoints/kazrcorge.c1-checkpoint.ipynb +++ /dev/null @@ -1,445 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "70840257-70e4-45e2-b491-14bff5a257a3", - "metadata": {}, - "source": [ - "# KAZRCORGE.C1 Plots\n", - "\n", - "[Click here](https://www.arm.gov/capabilities/vaps/kazrcor) for more information about this vap." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "460fd89f-e034-452c-b837-f65c5958264f", - "metadata": {}, - "outputs": [], - "source": [ - "%matplotlib widget\n", - "import ipywidgets as widgets\n", - "\n", - "import matplotlib.pyplot as plt\n", - "import ipywidgets as widgets\n", - "import numpy as np\n", - "import pandas as pd\n", - "import os\n", - "from datetime import datetime\n", - "\n", - "import act\n", - "import xarray as xr\n", - "\n", - "# Data archive directory\n", - "DATA_DIR = r'/data/archive/'\n", - "\n", - "# Datastream info\n", - "DATASTREAM_NAME = 'kazrcorge'\n", - "DATA_LEVEL = 'c1'\n", - "LOCATIONS = [{'end_date': '2014-02-07', 'facility': 'C1', 'site': 'nsa', 'start_date': '2011-11-11'}, {'end_date': '2014-03-15', 'facility': 'C1', 'site': 'sgp', 'start_date': '2011-01-18'}, {'end_date': '2014-03-16', 'facility': 'C1', 'site': 'twp', 'start_date': '2011-03-12'}, {'end_date': '2014-05-03', 'facility': 'C3', 'site': 'twp', 'start_date': '2011-01-27'}]" - ] - }, - { - "cell_type": "markdown", - "id": "9faaf875", - "metadata": {}, - "source": [ - "## Define site, facility, and date range" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "ac6764f5", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "The following locations and date ranges are available for this VAP:\n" - ] - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
sitefacilitystart_dateend_date
0nsaC12011-11-112014-02-07
1sgpC12011-01-182014-03-15
2twpC12011-03-122014-03-16
3twpC32011-01-272014-05-03
\n", - "
" - ], - "text/plain": [ - " site facility start_date end_date\n", - "0 nsa C1 2011-11-11 2014-02-07\n", - "1 sgp C1 2011-01-18 2014-03-15\n", - "2 twp C1 2011-03-12 2014-03-16\n", - "3 twp C3 2011-01-27 2014-05-03" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "print(\"The following locations and date ranges are available for this VAP:\")\n", - "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" - ] - }, - { - "cell_type": "markdown", - "id": "8d132223", - "metadata": {}, - "source": [ - "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "e563983a", - "metadata": {}, - "outputs": [], - "source": [ - "site_facility = ( 'sgp', 'C1' )\n", - "\n", - "date_start = '2014-03-14'\n", - "date_end = '2014-03-15'" - ] - }, - { - "cell_type": "markdown", - "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", - "metadata": {}, - "source": [ - "## Load data files\n", - "Load data files from /data/archive/" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'/data/archive/sgp/sgpkazrcorgeC1.c1'" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Compile list of files\n", - "site, facility = site_facility\n", - "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", - "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", - "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", - "dir_path\n" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "6be8f3dc", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "['20140314', '20140315']" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "from datetime import date, timedelta\n", - "import pandas as pd\n", - "\n", - "def get_ARM_formated_dates(start_date, end_date):\n", - " \"\"\"\n", - " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", - " EXAMPLE:\n", - " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", - " >> [\"20180219\", \"20180220\", \"20180221\"] \n", - " \"\"\"\n", - " \n", - " _start_date = pd.to_datetime(start_date)\n", - " _end_date = pd.to_datetime(end_date)\n", - " \n", - " delta = _end_date - _start_date # returns timedelta \n", - " dates = []\n", - "\n", - " for i in range(delta.days + 1):\n", - " day = _start_date + timedelta(days=i)\n", - " day_formated = day.strftime(format=\"%Y%m%d\")\n", - " dates.append(day_formated)\n", - " return dates\n", - "\n", - "\n", - "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "51feea2e", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "['/data/archive/sgp/sgpkazrcorgeC1.c1/sgpkazrcorgeC1.c1.20140314.000001.nc',\n", - " '/data/archive/sgp/sgpkazrcorgeC1.c1/sgpkazrcorgeC1.c1.20140315.000002.nc']" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Filter a list of files based on date pattern\n", - "import glob\n", - "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", - "files_filter = []\n", - "for date in dates:\n", - " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", - " files_filter\n", - "files_filter" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "b0e5d0d7", - "metadata": {}, - "outputs": [], - "source": [ - "# Load files as a single dataset\n", - "files_list = files_filter \n", - "ds = act.io.armfiles.read_netcdf(files_list)\n", - "ds.clean.cleanup()\n", - "print(f'{len(files_list)} files loaded')\n", - "ds\n" - ] - }, - { - "cell_type": "markdown", - "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", - "metadata": {}, - "source": [ - "## Plot time series data\n", - "#### Define the list of variables to be plotted:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d09b789e-84f1-4605-846b-a72c110c8048", - "metadata": {}, - "outputs": [], - "source": [ - "variables_to_plot = ['reflectivity', 'mean_doppler_velocity', 'spectral_width']" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", - "metadata": {}, - "outputs": [], - "source": [ - "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", - "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", - "\n", - "for i,v in enumerate(variables_to_plot):\n", - " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", - " ts_ax.grid()\n", - "\n", - "plt.show()\n" - ] - }, - { - "cell_type": "markdown", - "id": "194399aa-1907-452b-8ba9-bc31d7f60291", - "metadata": {}, - "source": [ - "## Quality check plots\n", - "#### Define variable for QC plot" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", - "metadata": {}, - "outputs": [], - "source": [ - "qc_variable = 'reflectivity'" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", - "metadata": {}, - "outputs": [], - "source": [ - "# QC Plot\n", - "if ('qc_'+qc_variable) in ds.variables:\n", - "\n", - " # Plot\n", - " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", - " qc_display.add_subplots((2,), figsize = (9.5,10))\n", - " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", - " qc_ax.grid()\n", - " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", - "\n", - " plt.show()\n", - "else:\n", - " print(f'QC not available for the selected field: {qc_variable}')\n" - ] - }, - { - "cell_type": "markdown", - "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", - "metadata": {}, - "source": [ - "## Field selection dropdown menu\n", - "Select variable to be plotted from a dropdown menu" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", - "metadata": {}, - "outputs": [], - "source": [ - "plt.ioff()\n", - "\n", - "# populate dropdown menu with available variables \n", - "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", - "d_variable = 'reflectivity'\n", - "dropdown = widgets.Dropdown(\n", - " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", - " value= d_variable,\n", - " description='Field:',\n", - " disabled=False,\n", - ")\n", - "dropdown.layout.margin = '0px 30% 0px 20%'\n", - "dropdown.layout.width = '50%'\n", - "\n", - "# set up display\n", - "i_display = act.plotting.TimeSeriesDisplay(ds)\n", - "i_display.add_subplots((1,), figsize = (9.5,5))\n", - "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", - "i_ax.grid()\n", - "i_fig = i_display.fig\n", - "\n", - "# update plot callback function\n", - "def update_plot(change):\n", - " i_ax.cla()\n", - " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", - " i_ax_new.grid()\n", - " i_fig.canvas.draw()\n", - " i_fig.canvas.flush_events()\n", - "\n", - "dropdown.observe(update_plot, names='value')\n", - "\n", - "widgets.AppLayout(\n", - " header=dropdown,\n", - " center=i_fig.canvas,\n", - " pane_heights=[1, 6,1]\n", - ")\n" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.16" - }, - "widgets": { - "application/vnd.jupyter.widget-state+json": { - "state": {}, - "version_major": 2, - "version_minor": 0 - } - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/VAPs/quicklook/KAZRCOR/.ipynb_checkpoints/kazrcorhi.c1-checkpoint.ipynb b/VAPs/quicklook/KAZRCOR/.ipynb_checkpoints/kazrcorhi.c1-checkpoint.ipynb deleted file mode 100644 index 5595ed42..00000000 --- a/VAPs/quicklook/KAZRCOR/.ipynb_checkpoints/kazrcorhi.c1-checkpoint.ipynb +++ /dev/null @@ -1,1856 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "70840257-70e4-45e2-b491-14bff5a257a3", - "metadata": {}, - "source": [ - "# KAZRCORHI.C1 Plots\n", - "\n", - "[Click here](https://www.arm.gov/capabilities/vaps/kazrcor) for more information about this vap." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "460fd89f-e034-452c-b837-f65c5958264f", - "metadata": {}, - "outputs": [], - "source": [ - "%matplotlib widget\n", - "import ipywidgets as widgets\n", - "\n", - "import matplotlib.pyplot as plt\n", - "import ipywidgets as widgets\n", - "import numpy as np\n", - "import pandas as pd\n", - "import os\n", - "from datetime import datetime\n", - "\n", - "import act\n", - "import xarray as xr\n", - "\n", - "# Data archive directory\n", - "DATA_DIR = r'/data/archive/'\n", - "\n", - "# Datastream info\n", - "DATASTREAM_NAME = 'kazrcorhi'\n", - "DATA_LEVEL = 'c1'\n", - "LOCATIONS = [{'end_date': '2014-03-16', 'facility': 'C1', 'site': 'twp', 'start_date': '2011-03-12'}, {'end_date': '2014-05-03', 'facility': 'C3', 'site': 'twp', 'start_date': '2011-01-27'}]" - ] - }, - { - "cell_type": "markdown", - "id": "9faaf875", - "metadata": {}, - "source": [ - "## Define site, facility, and date range" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "ac6764f5", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "The following locations and date ranges are available for this VAP:\n" - ] - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
sitefacilitystart_dateend_date
0twpC12011-03-122014-03-16
1twpC32011-01-272014-05-03
\n", - "
" - ], - "text/plain": [ - " site facility start_date end_date\n", - "0 twp C1 2011-03-12 2014-03-16\n", - "1 twp C3 2011-01-27 2014-05-03" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "print(\"The following locations and date ranges are available for this VAP:\")\n", - "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" - ] - }, - { - "cell_type": "markdown", - "id": "8d132223", - "metadata": {}, - "source": [ - "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "e563983a", - "metadata": {}, - "outputs": [], - "source": [ - "site_facility = ( 'twp', 'C1' )\n", - "\n", - "date_start = '2014-03-15'\n", - "date_end = '2014-03-16'" - ] - }, - { - "cell_type": "markdown", - "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", - "metadata": {}, - "source": [ - "## Load data files\n", - "Load data files from /data/archive/" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'/data/archive/twp/twpkazrcorhiC1.c1'" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Compile list of files\n", - "site, facility = site_facility\n", - "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", - "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", - "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", - "dir_path\n" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "6be8f3dc", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "['20140315', '20140316']" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "from datetime import date, timedelta\n", - "import pandas as pd\n", - "\n", - "def get_ARM_formated_dates(start_date, end_date):\n", - " \"\"\"\n", - " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", - " EXAMPLE:\n", - " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", - " >> [\"20180219\", \"20180220\", \"20180221\"] \n", - " \"\"\"\n", - " \n", - " _start_date = pd.to_datetime(start_date)\n", - " _end_date = pd.to_datetime(end_date)\n", - " \n", - " delta = _end_date - _start_date # returns timedelta \n", - " dates = []\n", - "\n", - " for i in range(delta.days + 1):\n", - " day = _start_date + timedelta(days=i)\n", - " day_formated = day.strftime(format=\"%Y%m%d\")\n", - " dates.append(day_formated)\n", - " return dates\n", - "\n", - "\n", - "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "51feea2e", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "['/data/archive/twp/twpkazrcorhiC1.c1/twpkazrcorhiC1.c1.20140315.000001.nc',\n", - " '/data/archive/twp/twpkazrcorhiC1.c1/twpkazrcorhiC1.c1.20140316.000001.nc']" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Filter a list of files based on date pattern\n", - "import glob\n", - "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", - "files_filter = []\n", - "for date in dates:\n", - " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", - " files_filter\n", - "files_filter" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "id": "b0e5d0d7", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "2 files loaded\n" - ] - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "
<xarray.Dataset>\n",
-       "Dimensions:                                   (time: 89645, range: 516)\n",
-       "Coordinates:\n",
-       "  * time                                      (time) datetime64[ns] 2014-03-1...\n",
-       "  * range                                     (range) float32 2.007e+03 ... 1...\n",
-       "Data variables: (12/23)\n",
-       "    base_time                                 (time) datetime64[ns] 2014-03-1...\n",
-       "    time_offset                               (time) datetime64[ns] 2014-03-1...\n",
-       "    reflectivity_copol                        (time, range) float32 dask.array<chunksize=(46771, 516), meta=np.ndarray>\n",
-       "    qc_reflectivity_copol                     (time, range) int32 dask.array<chunksize=(46771, 516), meta=np.ndarray>\n",
-       "    gaseous_attenuation_correction_copol      (time, range) float32 dask.array<chunksize=(46771, 516), meta=np.ndarray>\n",
-       "    qc_gaseous_attenuation_correction_copol   (time, range) int32 dask.array<chunksize=(46771, 516), meta=np.ndarray>\n",
-       "    ...                                        ...\n",
-       "    qc_rh                                     (time, range) int32 dask.array<chunksize=(46771, 516), meta=np.ndarray>\n",
-       "    bar_pres                                  (time, range) float32 dask.array<chunksize=(46771, 516), meta=np.ndarray>\n",
-       "    qc_bar_pres                               (time, range) int32 dask.array<chunksize=(46771, 516), meta=np.ndarray>\n",
-       "    lat                                       (time) float32 -2.06 ... -2.06\n",
-       "    lon                                       (time) float32 147.4 ... 147.4\n",
-       "    alt                                       (time) float32 4.0 4.0 ... 4.0 4.0\n",
-       "Attributes: (12/32)\n",
-       "    command_line:                idl -R -n kazrcor -s twp -f C1 -b 20140315 -...\n",
-       "    Conventions:                 ARM-1.1\n",
-       "    process_version:             vap-kazrcor-1.6-0.el6\n",
-       "    input_datastreams:           twpkazrgeC1.b1 : 1.3 : 20140315.000001\\ntwpk...\n",
-       "    dod_version:                 kazrcorhi-c1-1.3\n",
-       "    site_id:                     twp\n",
-       "    ...                          ...\n",
-       "    doi:                         10.5439/1228772\n",
-       "    history:                     created by user ttoto on machine chalk at 20...\n",
-       "    _file_dates:                 ['20140315', '20140316']\n",
-       "    _file_times:                 ['000001', '000001']\n",
-       "    _datastream:                 twpkazrcorhiC1.c1\n",
-       "    _arm_standards_flag:         1
" - ], - "text/plain": [ - "\n", - "Dimensions: (time: 89645, range: 516)\n", - "Coordinates:\n", - " * time (time) datetime64[ns] 2014-03-1...\n", - " * range (range) float32 2.007e+03 ... 1...\n", - "Data variables: (12/23)\n", - " base_time (time) datetime64[ns] 2014-03-1...\n", - " time_offset (time) datetime64[ns] 2014-03-1...\n", - " reflectivity_copol (time, range) float32 dask.array\n", - " qc_reflectivity_copol (time, range) int32 dask.array\n", - " gaseous_attenuation_correction_copol (time, range) float32 dask.array\n", - " qc_gaseous_attenuation_correction_copol (time, range) int32 dask.array\n", - " ... ...\n", - " qc_rh (time, range) int32 dask.array\n", - " bar_pres (time, range) float32 dask.array\n", - " qc_bar_pres (time, range) int32 dask.array\n", - " lat (time) float32 -2.06 ... -2.06\n", - " lon (time) float32 147.4 ... 147.4\n", - " alt (time) float32 4.0 4.0 ... 4.0 4.0\n", - "Attributes: (12/32)\n", - " command_line: idl -R -n kazrcor -s twp -f C1 -b 20140315 -...\n", - " Conventions: ARM-1.1\n", - " process_version: vap-kazrcor-1.6-0.el6\n", - " input_datastreams: twpkazrgeC1.b1 : 1.3 : 20140315.000001\\ntwpk...\n", - " dod_version: kazrcorhi-c1-1.3\n", - " site_id: twp\n", - " ... ...\n", - " doi: 10.5439/1228772\n", - " history: created by user ttoto on machine chalk at 20...\n", - " _file_dates: ['20140315', '20140316']\n", - " _file_times: ['000001', '000001']\n", - " _datastream: twpkazrcorhiC1.c1\n", - " _arm_standards_flag: 1" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Load files as a single dataset\n", - "files_list = files_filter \n", - "ds = act.io.armfiles.read_netcdf(files_list)\n", - "ds.clean.cleanup()\n", - "print(f'{len(files_list)} files loaded')\n", - "ds\n" - ] - }, - { - "cell_type": "markdown", - "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", - "metadata": {}, - "source": [ - "## Plot time series data\n", - "#### Define the list of variables to be plotted:" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "id": "d09b789e-84f1-4605-846b-a72c110c8048", - "metadata": {}, - "outputs": [], - "source": [ - "variables_to_plot = ['reflectivity', 'mean_doppler_velocity', 'spectral_width']" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", - "metadata": {}, - "outputs": [ - { - "ename": "KeyError", - "evalue": "'reflectivity'", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mKeyError\u001b[0m Traceback (most recent call last)", - "Cell \u001b[0;32mIn[9], line 5\u001b[0m\n\u001b[1;32m 2\u001b[0m ts_display\u001b[38;5;241m.\u001b[39madd_subplots((\u001b[38;5;28mlen\u001b[39m(variables_to_plot),), figsize \u001b[38;5;241m=\u001b[39m (\u001b[38;5;241m9.5\u001b[39m,\u001b[38;5;241m4\u001b[39m\u001b[38;5;241m*\u001b[39m\u001b[38;5;28mlen\u001b[39m(variables_to_plot)))\n\u001b[1;32m 4\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m i,v \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28menumerate\u001b[39m(variables_to_plot):\n\u001b[0;32m----> 5\u001b[0m ts_ax \u001b[38;5;241m=\u001b[39m ts_display\u001b[38;5;241m.\u001b[39mplot(v, subplot_index\u001b[38;5;241m=\u001b[39m(i,), set_title\u001b[38;5;241m=\u001b[39m\u001b[43mds\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mvariables\u001b[49m\u001b[43m[\u001b[49m\u001b[43mv\u001b[49m\u001b[43m]\u001b[49m\u001b[38;5;241m.\u001b[39mattrs[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mlong_name\u001b[39m\u001b[38;5;124m'\u001b[39m],)\n\u001b[1;32m 6\u001b[0m ts_ax\u001b[38;5;241m.\u001b[39mgrid()\n\u001b[1;32m 8\u001b[0m plt\u001b[38;5;241m.\u001b[39mshow()\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/core/utils.py:453\u001b[0m, in \u001b[0;36mFrozen.__getitem__\u001b[0;34m(self, key)\u001b[0m\n\u001b[1;32m 452\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21m__getitem__\u001b[39m(\u001b[38;5;28mself\u001b[39m, key: K) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m V:\n\u001b[0;32m--> 453\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mmapping\u001b[49m\u001b[43m[\u001b[49m\u001b[43mkey\u001b[49m\u001b[43m]\u001b[49m\n", - "\u001b[0;31mKeyError\u001b[0m: 'reflectivity'" - ] - }, - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "f666994b48ed49da969503777e133ba7", - "version_major": 2, - "version_minor": 0 - }, - "image/png": "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1tnZmUVE9thjj2WdnZ3ZW2+9lWWZ6+/JJmwT9uSTT2aVlZVZYWFhNn/+/Gznzp3D/+zuu+/ObrrpphHH/+d//mf2J3/yJ1lhYWH2uc99Ltu8eXOOJ2aqGs9euummm7KIGPW4++67cz84U854f1/6XcKW3zXevXTw4MHs1ltvzS677LJs9uzZWXNzc/bBBx/keGqmqvHup8cffzz74he/mF122WVZeXl59ld/9VfZ0aNHczw1U81//Md/fOw1kOvvyZWXZd5TAQAAQLp8xhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAnbCbZr165YtmxZzJo1K/Ly8uKVV175xHN27twZ1dXVUVxcHNddd1089dRTEz8oAABAooTtBHv//fdj3rx58cQTT5zT8YcPH46lS5dGXV1ddHZ2xkMPPRSrVq2KF198cYInBQAASFNelmXZZA9xqcjLy4uXX345li9fftZjvv3tb8err74aBw8eHF5ramqKn/3sZ7Fv374cTAkAAJCWgskegJH27dsX9fX1I9Zuv/322LJlS3z44Ycxffr0Mc8bGhqKoaGh4eenT5+Od999N2bMmBF5eXkTOjMAAFzqsiyL48ePx6xZs2LaNG+MzTVhO8X09vZGWVnZiLWysrI4efJk9PX1RXl5+ZjntbS0xIYNG3IxIgAAcBZHjhyJ2bNnT/YYlxxhOwX9/h3WM+8W/7g7r+vWrYvm5ubh5wMDA3HttdfGkSNHoqSkZGIGBQAAIiJicHAwKioq4g//8A8ne5RLkrCdYq6++uro7e0dsXbs2LEoKCiIGTNmnPW8oqKiKCoqGrVeUlIibAEAIEd8DHByePP3FLNo0aJob28fsbZjx46oqak56+drAQAALmXCdoK999570dXVFV1dXRHx0df5dHV1RXd3d0R89BbixsbG4eObmprirbfeiubm5jh48GBs3bo1tmzZEg888MBkjA8AADDleSvyBNu/f3/cfPPNw8/P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7dixY1FQUBAzZsw463lFRUVRVFQ0ar2kpETYAgBAjvgY4OTw5u8pZtGiRdHe3j5ibceOHVFTU3PWz9cCAABcyoTtBHvvvfeiq6srurq6IuKjr/Pp6uqK7u7uiPjoLcSNjY3Dxzc1NcVbb70Vzc3NcfDgwdi6dWts2bIlHnjggckYHwAAYMrzVuQJtn///rj55puHn5/5HOzdd98d27Zti56enuHIjYioqqqKtra2WLNmTTz55JMxa9asePzxx+NrX/tazmcHAABIge+xvUgNDg5GaWlpDAwM+IwtAABMMNffk8tbkQEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAnbHNm0aVNUVVVFcXFxVFdXx+7duz/2+O3bt8e8efPi8ssvj/Ly8rj33nujv78/R9MCAACkQ9jmQGtra6xevTrWr18fnZ2dUVdXF0uWLInu7u4xj9+zZ080NjbGypUr44033ojnn38+fvrTn8Z9992X48kBAACmPmGbA4899lisXLky7rvvvpgzZ0780z/9U1RUVMTmzZvHPP6//uu/4nOf+1ysWrUqqqqq4k//9E/jG9/4Ruzfvz/HkwMAAEx9wnaCnThxIjo6OqK+vn7Een19fezdu3fMc2pra+Po0aPR1tYWWZbFO++8Ey+88ELccccduRgZAAAgKcJ2gvX19cWpU6eirKxsxHpZWVn09vaOeU5tbW1s3749GhoaorCwMK6++ur4zGc+E9///vfP+nOGhoZicHBwxAMAAOBSIGxzJC8vb8TzLMtGrZ1x4MCBWLVqVTz88MPR0dERr732Whw+fDiamprO+uu3tLREaWnp8KOiouKCzg8AADBV5WVZlk32EBezEydOxOWXXx7PP/98/MVf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yZlq3n///Zg3b1488cQT53S86+8cy0jSggULsqamphFrN954Y7Z27doxj/+7v/u77MYbbxyx9o1vfCP7yle+MmEzkobx7qWxfPGLX8w2bNhwoUcjMee7lxoaGrK///u/zx555JFs3rx5EzghqRjvXvq3f/u3rLS0NOvv78/FeCRmvPvpH//xH7PrrrtuxNrjjz+ezZ49e8JmJD0Rkb388ssfe4zr79xyxzZBJ06ciI6Ojqivrx+xXl9fH3v37h3znH379o06/vbbb4/9+/fHhx9+OGGzMrWdz176fadPn47jx4/HlVdeOREjkojz3UvPPPNMvPnmm/HII49M9Igk4nz20quvvho1NTXx3e9+N6655pq44YYb4oEHHojf/OY3uRiZKex89lNtbW0cPXo02traIsuyeOedd+KFF16IO+64IxcjcxFx/Z1bBZM9AOPX19cXp06dirKyshHrZWVl0dvbO+Y5vb29Yx5/8uTJ6Ovri/Ly8gmbl6nrfPbS7/ve974X77//fqxYsWIiRiQR57OXfvnLX8batWtj9+7dUVDgP0d85Hz20qFDh2LPnj1RXFwcL7/8cvT19cX9998f7777rs/ZXuLOZz/V1tbG9u3bo6GhIX7729/GyZMn48/+7M/i+9//fi5G5iLi+ju33LFNWF5e3ojnWZaNWvuk48da59Iz3r10xnPPPRff+c53orW1Na666qqJGo+EnOteOnXqVNx5552xYcOGuOGGG3I1HgkZz+9Lp0+fjry8vNi+fXssWLAgli5dGo899lhs27bNXVsiYnz76cCBA7Fq1ap4+OGHo6OjI1577bU4fPhwNDU15WJULjKuv3PHH5EnaObMmZGfnz/qTxqPHTs26k+Fzrj66qvHPL6goCBmzJgxYbMytZ3PXjqjtbU1Vq5cGc8//3zceuutEzkmCRjvXjp+/Hjs378/Ojs741vf+lZEfBQnWZZFQUFB7NixI2655ZaczM7Ucj6/L5WXl8c111wTpaWlw2tz5syJLMvi6NGjcf3110/ozExd57OfWlpaYvHixfHggw9GRMSXvvSluOKKK6Kuri4effRRd9k4Z66/c8sd2wQVFhZGdXV1tLe3j1hvb2+P2traMc9ZtGjRqON37NgRNTU1MX369AmblantfPZSxEd3au+555549tlnfeaIiBj/XiopKYmf//zn0dXVNfxoamqKL3zhC9HV1RULFy7M1ehMMefz+9LixYvj7bffjvfee2947Re/+EVMmzYtZs+ePaHzMrWdz3764IMPYtq0kZfI+fn5EfF/d9vgXLj+zrFJ+kur+JR+/OMfZ9OnT8+2bNmSHThwIFu9enV2xRVXZP/7v/+bZVmWrV27NrvrrruGjz906FB2+eWXZ2vWrMkOHDiQbdmyJZs+fXr2wgsvTNZLYIoY71569tlns4KCguzJJ5/Menp6hh+//vWvJ+slMEWMdy/9Pn8rMmeMdy8dP348mz17dvaXf/mX2RtvvJHt3Lkzu/7667P77rtvsl4CU8h499MzzzyTFRQUZJs2bcrefPPNbM+ePVlNTU22YMGCyXoJTBHHjx/POjs7s87Oziwissceeyzr7OzM3nrrrSzLXH9PNmGbsCeffDKrrKzMCgsLs/nz52c7d+4c/md33313dtNNN404/j//8z+zP/mTP8kKCwuzz33uc9nmzZtzPDFT1Xj20k033ZRFxKjH3XffnfvBmXLG+/vS7xK2/K7x7qWDBw9mt956a3bZZZdls2fPzpqbm7MPPvggx1MzVY13Pz3++OPZF7/4xeyyyy7LysvLs7/6q7/Kjh49muOpmWr+4z/+42OvgVx/T668LPOeCgAAANLlM7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQtP8PDcrXAZbhxkUAAAAASUVORK5CYII=", - "text/html": [ - "\n", - "
\n", - "
\n", - " Figure\n", - "
\n", - " \n", - "
\n", - " " - ], - "text/plain": [ - "Canvas(toolbar=Toolbar(toolitems=[('Home', 'Reset original view', 'home', 'home'), ('Back', 'Back to previous …" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", - "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", - "\n", - "for i,v in enumerate(variables_to_plot):\n", - " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", - " ts_ax.grid()\n", - "\n", - "plt.show()\n" - ] - }, - { - "cell_type": "markdown", - "id": "194399aa-1907-452b-8ba9-bc31d7f60291", - "metadata": {}, - "source": [ - "## Quality check plots\n", - "#### Define variable for QC plot" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", - "metadata": {}, - "outputs": [], - "source": [ - "qc_variable = 'reflectivity'" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", - "metadata": {}, - "outputs": [], - "source": [ - "# QC Plot\n", - "if ('qc_'+qc_variable) in ds.variables:\n", - "\n", - " # Plot\n", - " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", - " qc_display.add_subplots((2,), figsize = (9.5,10))\n", - " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", - " qc_ax.grid()\n", - " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", - "\n", - " plt.show()\n", - "else:\n", - " print(f'QC not available for the selected field: {qc_variable}')\n" - ] - }, - { - "cell_type": "markdown", - "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", - "metadata": {}, - "source": [ - "## Field selection dropdown menu\n", - "Select variable to be plotted from a dropdown menu" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", - "metadata": {}, - "outputs": [], - "source": [ - "plt.ioff()\n", - "\n", - "# populate dropdown menu with available variables \n", - "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", - "d_variable = 'reflectivity'\n", - "dropdown = widgets.Dropdown(\n", - " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", - " value= d_variable,\n", - " description='Field:',\n", - " disabled=False,\n", - ")\n", - "dropdown.layout.margin = '0px 30% 0px 20%'\n", - "dropdown.layout.width = '50%'\n", - "\n", - "# set up display\n", - "i_display = act.plotting.TimeSeriesDisplay(ds)\n", - "i_display.add_subplots((1,), figsize = (9.5,5))\n", - "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", - "i_ax.grid()\n", - "i_fig = i_display.fig\n", - "\n", - "# update plot callback function\n", - "def update_plot(change):\n", - " i_ax.cla()\n", - " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", - " i_ax_new.grid()\n", - " i_fig.canvas.draw()\n", - " i_fig.canvas.flush_events()\n", - "\n", - "dropdown.observe(update_plot, names='value')\n", - "\n", - "widgets.AppLayout(\n", - " header=dropdown,\n", - " center=i_fig.canvas,\n", - " pane_heights=[1, 6,1]\n", - ")\n" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.16" - }, - "widgets": { - "application/vnd.jupyter.widget-state+json": { - "state": {}, - "version_major": 2, - "version_minor": 0 - } - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/VAPs/quicklook/KAZRCOR/.ipynb_checkpoints/kazrcormd.c1-checkpoint.ipynb b/VAPs/quicklook/KAZRCOR/.ipynb_checkpoints/kazrcormd.c1-checkpoint.ipynb deleted file mode 100644 index 284e0d7b..00000000 --- a/VAPs/quicklook/KAZRCOR/.ipynb_checkpoints/kazrcormd.c1-checkpoint.ipynb +++ /dev/null @@ -1,2667 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "70840257-70e4-45e2-b491-14bff5a257a3", - "metadata": {}, - "source": [ - "# KAZRCORMD.C1 Plots\n", - "\n", - "[Click here](https://www.arm.gov/capabilities/vaps/kazrcor) for more information about this vap." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "460fd89f-e034-452c-b837-f65c5958264f", - "metadata": {}, - "outputs": [], - "source": [ - "%matplotlib widget\n", - "import ipywidgets as widgets\n", - "\n", - "import matplotlib.pyplot as plt\n", - "import ipywidgets as widgets\n", - "import numpy as np\n", - "import pandas as pd\n", - "import os\n", - "from datetime import datetime\n", - "\n", - "import act\n", - "import xarray as xr\n", - "\n", - "# Data archive directory\n", - "DATA_DIR = r'/data/archive/'\n", - "\n", - "# Datastream info\n", - "DATASTREAM_NAME = 'kazrcormd'\n", - "DATA_LEVEL = 'c1'\n", - "LOCATIONS = [{'end_date': '2014-02-07', 'facility': 'C1', 'site': 'nsa', 'start_date': '2011-11-11'}, {'end_date': '2014-03-15', 'facility': 'C1', 'site': 'sgp', 'start_date': '2011-05-03'}]" - ] - }, - { - "cell_type": "markdown", - "id": "9faaf875", - "metadata": {}, - "source": [ - "## Define site, facility, and date range" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "ac6764f5", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "The following locations and date ranges are available for this VAP:\n" - ] - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
sitefacilitystart_dateend_date
0nsaC12011-11-112014-02-07
1sgpC12011-05-032014-03-15
\n", - "
" - ], - "text/plain": [ - " site facility start_date end_date\n", - "0 nsa C1 2011-11-11 2014-02-07\n", - "1 sgp C1 2011-05-03 2014-03-15" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "print(\"The following locations and date ranges are available for this VAP:\")\n", - "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" - ] - }, - { - "cell_type": "markdown", - "id": "8d132223", - "metadata": {}, - "source": [ - "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "e563983a", - "metadata": {}, - "outputs": [], - "source": [ - "site_facility = ( 'sgp', 'C1' )\n", - "\n", - "date_start = '2014-03-14'\n", - "date_end = '2014-03-15'" - ] - }, - { - "cell_type": "markdown", - "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", - "metadata": {}, - "source": [ - "## Load data files\n", - "Load data files from /data/archive/" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'/data/archive/sgp/sgpkazrcormdC1.c1'" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Compile list of files\n", - "site, facility = site_facility\n", - "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", - "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", - "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", - "dir_path\n" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "6be8f3dc", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "['20140314', '20140315']" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "from datetime import date, timedelta\n", - "import pandas as pd\n", - "\n", - "def get_ARM_formated_dates(start_date, end_date):\n", - " \"\"\"\n", - " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", - " EXAMPLE:\n", - " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", - " >> [\"20180219\", \"20180220\", \"20180221\"] \n", - " \"\"\"\n", - " \n", - " _start_date = pd.to_datetime(start_date)\n", - " _end_date = pd.to_datetime(end_date)\n", - " \n", - " delta = _end_date - _start_date # returns timedelta \n", - " dates = []\n", - "\n", - " for i in range(delta.days + 1):\n", - " day = _start_date + timedelta(days=i)\n", - " day_formated = day.strftime(format=\"%Y%m%d\")\n", - " dates.append(day_formated)\n", - " return dates\n", - "\n", - "\n", - "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "51feea2e", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "['/data/archive/sgp/sgpkazrcormdC1.c1/sgpkazrcormdC1.c1.20140314.000001.nc',\n", - " '/data/archive/sgp/sgpkazrcormdC1.c1/sgpkazrcormdC1.c1.20140315.000002.nc']" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Filter a list of files based on date pattern\n", - "import glob\n", - "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", - "files_filter = []\n", - "for date in dates:\n", - " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", - " files_filter\n", - "files_filter" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "id": "b0e5d0d7", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "72 files loaded\n" - ] - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "
<xarray.Dataset>\n",
-       "Dimensions:                                   (time: 23386, range: 652)\n",
-       "Coordinates:\n",
-       "  * time                                      (time) datetime64[ns] 2014-03-1...\n",
-       "  * range                                     (range) float32 718.1 ... 2.023...\n",
-       "Data variables: (12/39)\n",
-       "    base_time                                 datetime64[ns] 2014-03-14\n",
-       "    time_offset                               (time) datetime64[ns] 2014-03-1...\n",
-       "    reflectivity_copol                        (time, range) float32 dask.array<chunksize=(23386, 652), meta=np.ndarray>\n",
-       "    qc_reflectivity_copol                     (time, range) int32 dask.array<chunksize=(23386, 652), meta=np.ndarray>\n",
-       "    gaseous_attenuation_correction_copol      (time, range) float32 dask.array<chunksize=(23386, 652), meta=np.ndarray>\n",
-       "    qc_gaseous_attenuation_correction_copol   (time, range) int32 dask.array<chunksize=(23386, 652), meta=np.ndarray>\n",
-       "    ...                                        ...\n",
-       "    qc_rh                                     (time, range) int32 dask.array<chunksize=(23386, 652), meta=np.ndarray>\n",
-       "    bar_pres                                  (time, range) float32 dask.array<chunksize=(23386, 652), meta=np.ndarray>\n",
-       "    qc_bar_pres                               (time, range) int32 dask.array<chunksize=(23386, 652), meta=np.ndarray>\n",
-       "    lat                                       float32 ...\n",
-       "    lon                                       float32 ...\n",
-       "    alt                                       float32 ...\n",
-       "Attributes: (12/33)\n",
-       "    command_line:                idl -R -n kazrcor -s sgp -f C1 -b 20140314 -...\n",
-       "    Conventions:                 ARM-1.1\n",
-       "    process_version:             vap-kazrcor-1.6-0.el6\n",
-       "    input_datastreams:           sgpkazrgeC1.b1 : 1.3 : 20140314.000001\\nsgpk...\n",
-       "    dod_version:                 kazrcormd-c1-2.0\n",
-       "    site_id:                     sgp\n",
-       "    ...                          ...\n",
-       "    doi:                         10.5439/1228771\n",
-       "    history:                     created by user ttoto on machine chalk at 20...\n",
-       "    _file_dates:                 ['20140314']\n",
-       "    _file_times:                 ['000001']\n",
-       "    _datastream:                 sgpkazrcormdC1.c1\n",
-       "    _arm_standards_flag:         1
" - ], - "text/plain": [ - "\n", - "Dimensions: (time: 23386, range: 652)\n", - "Coordinates:\n", - " * time (time) datetime64[ns] 2014-03-1...\n", - " * range (range) float32 718.1 ... 2.023...\n", - "Data variables: (12/39)\n", - " base_time datetime64[ns] 2014-03-14\n", - " time_offset (time) datetime64[ns] 2014-03-1...\n", - " reflectivity_copol (time, range) float32 dask.array\n", - " qc_reflectivity_copol (time, range) int32 dask.array\n", - " gaseous_attenuation_correction_copol (time, range) float32 dask.array\n", - " qc_gaseous_attenuation_correction_copol (time, range) int32 dask.array\n", - " ... ...\n", - " qc_rh (time, range) int32 dask.array\n", - " bar_pres (time, range) float32 dask.array\n", - " qc_bar_pres (time, range) int32 dask.array\n", - " lat float32 ...\n", - " lon float32 ...\n", - " alt float32 ...\n", - "Attributes: (12/33)\n", - " command_line: idl -R -n kazrcor -s sgp -f C1 -b 20140314 -...\n", - " Conventions: ARM-1.1\n", - " process_version: vap-kazrcor-1.6-0.el6\n", - " input_datastreams: sgpkazrgeC1.b1 : 1.3 : 20140314.000001\\nsgpk...\n", - " dod_version: kazrcormd-c1-2.0\n", - " site_id: sgp\n", - " ... ...\n", - " doi: 10.5439/1228771\n", - " history: created by user ttoto on machine chalk at 20...\n", - " _file_dates: ['20140314']\n", - " _file_times: ['000001']\n", - " _datastream: sgpkazrcormdC1.c1\n", - " _arm_standards_flag: 1" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Load files as a single dataset\n", - "files_list = files_filter [0]\n", - "ds = act.io.armfiles.read_netcdf(files_list)\n", - "ds.clean.cleanup()\n", - "print(f'{len(files_list)} files loaded')\n", - "ds\n" - ] - }, - { - "cell_type": "markdown", - "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", - "metadata": {}, - "source": [ - "## Plot time series data\n", - "#### Define the list of variables to be plotted:" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "id": "d09b789e-84f1-4605-846b-a72c110c8048", - "metadata": {}, - "outputs": [], - "source": [ - "variables_to_plot = ['reflectivity', 'mean_doppler_velocity', 'spectral_width']" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", - "metadata": {}, - "outputs": [ - { - "ename": "KeyError", - "evalue": "'reflectivity'", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mKeyError\u001b[0m Traceback (most recent call last)", - "Cell \u001b[0;32mIn[9], line 5\u001b[0m\n\u001b[1;32m 2\u001b[0m ts_display\u001b[38;5;241m.\u001b[39madd_subplots((\u001b[38;5;28mlen\u001b[39m(variables_to_plot),), figsize \u001b[38;5;241m=\u001b[39m (\u001b[38;5;241m9.5\u001b[39m,\u001b[38;5;241m4\u001b[39m\u001b[38;5;241m*\u001b[39m\u001b[38;5;28mlen\u001b[39m(variables_to_plot)))\n\u001b[1;32m 4\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m i,v \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28menumerate\u001b[39m(variables_to_plot):\n\u001b[0;32m----> 5\u001b[0m ts_ax \u001b[38;5;241m=\u001b[39m ts_display\u001b[38;5;241m.\u001b[39mplot(v, subplot_index\u001b[38;5;241m=\u001b[39m(i,), set_title\u001b[38;5;241m=\u001b[39m\u001b[43mds\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mvariables\u001b[49m\u001b[43m[\u001b[49m\u001b[43mv\u001b[49m\u001b[43m]\u001b[49m\u001b[38;5;241m.\u001b[39mattrs[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mlong_name\u001b[39m\u001b[38;5;124m'\u001b[39m],)\n\u001b[1;32m 6\u001b[0m ts_ax\u001b[38;5;241m.\u001b[39mgrid()\n\u001b[1;32m 8\u001b[0m plt\u001b[38;5;241m.\u001b[39mshow()\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/core/utils.py:453\u001b[0m, in \u001b[0;36mFrozen.__getitem__\u001b[0;34m(self, key)\u001b[0m\n\u001b[1;32m 452\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21m__getitem__\u001b[39m(\u001b[38;5;28mself\u001b[39m, key: K) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m V:\n\u001b[0;32m--> 453\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mmapping\u001b[49m\u001b[43m[\u001b[49m\u001b[43mkey\u001b[49m\u001b[43m]\u001b[49m\n", - "\u001b[0;31mKeyError\u001b[0m: 'reflectivity'" - ] - }, - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "fe6a6740ed58487aa977d2bfc40e0bdc", - "version_major": 2, - "version_minor": 0 - }, - "image/png": "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1tnZmUVE9thjj2WdnZ3ZW2+9lWWZ6+/JJmwT9uSTT2aVlZVZYWFhNn/+/Gznzp3D/+zuu+/ObrrpphHH/+d//mf2J3/yJ1lhYWH2uc99Ltu8eXOOJ2aqGs9euummm7KIGPW4++67cz84U854f1/6XcKW3zXevXTw4MHs1ltvzS677LJs9uzZWXNzc/bBBx/keGqmqvHup8cffzz74he/mF122WVZeXl59ld/9VfZ0aNHczw1U81//Md/fOw1kOvvyZWXZd5TAQAAQLp8xhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAnbCbZr165YtmxZzJo1K/Ly8uKVV175xHN27twZ1dXVUVxcHNddd1089dRTEz8oAABAooTtBHv//fdj3rx58cQTT5zT8YcPH46lS5dGXV1ddHZ2xkMPPRSrVq2KF198cYInBQAASFNelmXZZA9xqcjLy4uXX345li9fftZjvv3tb8err74aBw8eHF5ramqKn/3sZ7Fv374cTAkAAJCWgskegJH27dsX9fX1I9Zuv/322LJlS3z44Ycxffr0Mc8bGhqKoaGh4eenT5+Od999N2bMmBF5eXkTOjMAAFzqsiyL48ePx6xZs2LaNG+MzTVhO8X09vZGWVnZiLWysrI4efJk9PX1RXl5+ZjntbS0xIYNG3IxIgAAcBZHjhyJ2bNnT/YYlxxhOwX9/h3WM+8W/7g7r+vWrYvm5ubh5wMDA3HttdfGkSNHoqSkZGIGBQAAIiJicHAwKioq4g//8A8ne5RLkrCdYq6++uro7e0dsXbs2LEoKCiIGTNmnPW8oqKiKCoqGrVeUlIibAEAIEd8DHByePP3FLNo0aJob28fsbZjx46oqak56+drAQAALmXCdoK999570dXVFV1dXRHx0df5dHV1RXd3d0R89BbixsbG4eObmprirbfeiubm5jh48GBs3bo1tmzZEg888MBkjA8AADDleSvyBNu/f3/cfPPNw8/P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7dixY1FQUBAzZsw463lFRUVRVFQ0ar2kpETYAgBAjvgY4OTw5u8pZtGiRdHe3j5ibceOHVFTU3PWz9cCAABcyoTtBHvvvfeiq6srurq6IuKjr/Pp6uqK7u7uiPjoLcSNjY3Dxzc1NcVbb70Vzc3NcfDgwdi6dWts2bIlHnjggckYHwAAYMrzVuQJtn///rj55puHn5/5HOzdd98d27Zti56enuHIjYioqqqKtra2WLNmTTz55JMxa9asePzxx+NrX/tazmcHAABIge+xvUgNDg5GaWlpDAwM+IwtAABMMNffk8tbkQEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAnbHNm0aVNUVVVFcXFxVFdXx+7duz/2+O3bt8e8efPi8ssvj/Ly8rj33nujv78/R9MCAACkQ9jmQGtra6xevTrWr18fnZ2dUVdXF0uWLInu7u4xj9+zZ080NjbGypUr44033ojnn38+fvrTn8Z9992X48kBAACmPmGbA4899lisXLky7rvvvpgzZ0780z/9U1RUVMTmzZvHPP6//uu/4nOf+1ysWrUqqqqq4k//9E/jG9/4Ruzfvz/HkwMAAEx9wnaCnThxIjo6OqK+vn7Een19fezdu3fMc2pra+Po0aPR1tYWWZbFO++8Ey+88ELccccduRgZAAAgKcJ2gvX19cWpU6eirKxsxHpZWVn09vaOeU5tbW1s3749GhoaorCwMK6++ur4zGc+E9///vfP+nOGhoZicHBwxAMAAOBSIGxzJC8vb8TzLMtGrZ1x4MCBWLVqVTz88MPR0dERr732Whw+fDiamprO+uu3tLREaWnp8KOiouKCzg8AADBV5WVZlk32EBezEydOxOWXXx7PP/98/MVf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yZlq3n///Zg3b1488cQT53S86+8cy0jSggULsqamphFrN954Y7Z27doxj/+7v/u77MYbbxyx9o1vfCP7yle+MmEzkobx7qWxfPGLX8w2bNhwoUcjMee7lxoaGrK///u/zx555JFs3rx5EzghqRjvXvq3f/u3rLS0NOvv78/FeCRmvPvpH//xH7PrrrtuxNrjjz+ezZ49e8JmJD0Rkb388ssfe4zr79xyxzZBJ06ciI6Ojqivrx+xXl9fH3v37h3znH379o06/vbbb4/9+/fHhx9+OGGzMrWdz176fadPn47jx4/HlVdeOREjkojz3UvPPPNMvPnmm/HII49M9Igk4nz20quvvho1NTXx3e9+N6655pq44YYb4oEHHojf/OY3uRiZKex89lNtbW0cPXo02traIsuyeOedd+KFF16IO+64IxcjcxFx/Z1bBZM9AOPX19cXp06dirKyshHrZWVl0dvbO+Y5vb29Yx5/8uTJ6Ovri/Ly8gmbl6nrfPbS7/ve974X77//fqxYsWIiRiQR57OXfvnLX8batWtj9+7dUVDgP0d85Hz20qFDh2LPnj1RXFwcL7/8cvT19cX9998f7777rs/ZXuLOZz/V1tbG9u3bo6GhIX7729/GyZMn48/+7M/i+9//fi5G5iLi+ju33LFNWF5e3ojnWZaNWvuk48da59Iz3r10xnPPPRff+c53orW1Na666qqJGo+EnOteOnXqVNx5552xYcOGuOGGG3I1HgkZz+9Lp0+fjry8vNi+fXssWLAgli5dGo899lhs27bNXVsiYnz76cCBA7Fq1ap4+OGHo6OjI1577bU4fPhwNDU15WJULjKuv3PHH5EnaObMmZGfnz/qTxqPHTs26k+Fzrj66qvHPL6goCBmzJgxYbMytZ3PXjqjtbU1Vq5cGc8//3zceuutEzkmCRjvXjp+/Hjs378/Ojs741vf+lZEfBQnWZZFQUFB7NixI2655ZaczM7Ucj6/L5WXl8c111wTpaWlw2tz5syJLMvi6NGjcf3110/ozExd57OfWlpaYvHixfHggw9GRMSXvvSluOKKK6Kuri4effRRd9k4Z66/c8sd2wQVFhZGdXV1tLe3j1hvb2+P2traMc9ZtGjRqON37NgRNTU1MX369AmblantfPZSxEd3au+555549tlnfeaIiBj/XiopKYmf//zn0dXVNfxoamqKL3zhC9HV1RULFy7M1ehMMefz+9LixYvj7bffjvfee2947Re/+EVMmzYtZs+ePaHzMrWdz3764IMPYtq0kZfI+fn5EfF/d9vgXLj+zrFJ+kur+JR+/OMfZ9OnT8+2bNmSHThwIFu9enV2xRVXZP/7v/+bZVmWrV27NrvrrruGjz906FB2+eWXZ2vWrMkOHDiQbdmyJZs+fXr2wgsvTNZLYIoY71569tlns4KCguzJJ5/Menp6hh+//vWvJ+slMEWMdy/9Pn8rMmeMdy8dP348mz17dvaXf/mX2RtvvJHt3Lkzu/7667P77rtvsl4CU8h499MzzzyTFRQUZJs2bcrefPPNbM+ePVlNTU22YMGCyXoJTBHHjx/POjs7s87Oziwissceeyzr7OzM3nrrrSzLXH9PNmGbsCeffDKrrKzMCgsLs/nz52c7d+4c/md33313dtNNN404/j//8z+zP/mTP8kKCwuzz33uc9nmzZtzPDFT1Xj20k033ZRFxKjH3XffnfvBmXLG+/vS7xK2/K7x7qWDBw9mt956a3bZZZdls2fPzpqbm7MPPvggx1MzVY13Pz3++OPZF7/4xeyyyy7LysvLs7/6q7/Kjh49muOpmWr+4z/+42OvgVx/T668LPOeCgAAANLlM7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQtP8PDcrXAZbhxkUAAAAASUVORK5CYII=", - "text/html": [ - "\n", - "
\n", - "
\n", - " Figure\n", - "
\n", - " \n", - "
\n", - " " - ], - "text/plain": [ - "Canvas(toolbar=Toolbar(toolitems=[('Home', 'Reset original view', 'home', 'home'), ('Back', 'Back to previous …" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", - "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", - "\n", - "for i,v in enumerate(variables_to_plot):\n", - " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", - " ts_ax.grid()\n", - "\n", - "plt.show()\n" - ] - }, - { - "cell_type": "markdown", - "id": "194399aa-1907-452b-8ba9-bc31d7f60291", - "metadata": {}, - "source": [ - "## Quality check plots\n", - "#### Define variable for QC plot" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", - "metadata": {}, - "outputs": [], - "source": [ - "qc_variable = 'reflectivity'" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", - "metadata": {}, - "outputs": [], - "source": [ - "# QC Plot\n", - "if ('qc_'+qc_variable) in ds.variables:\n", - "\n", - " # Plot\n", - " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", - " qc_display.add_subplots((2,), figsize = (9.5,10))\n", - " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", - " qc_ax.grid()\n", - " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", - "\n", - " plt.show()\n", - "else:\n", - " print(f'QC not available for the selected field: {qc_variable}')\n" - ] - }, - { - "cell_type": "markdown", - "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", - "metadata": {}, - "source": [ - "## Field selection dropdown menu\n", - "Select variable to be plotted from a dropdown menu" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", - "metadata": {}, - "outputs": [], - "source": [ - "plt.ioff()\n", - "\n", - "# populate dropdown menu with available variables \n", - "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", - "d_variable = 'reflectivity'\n", - "dropdown = widgets.Dropdown(\n", - " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", - " value= d_variable,\n", - " description='Field:',\n", - " disabled=False,\n", - ")\n", - "dropdown.layout.margin = '0px 30% 0px 20%'\n", - "dropdown.layout.width = '50%'\n", - "\n", - "# set up display\n", - "i_display = act.plotting.TimeSeriesDisplay(ds)\n", - "i_display.add_subplots((1,), figsize = (9.5,5))\n", - "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", - "i_ax.grid()\n", - "i_fig = i_display.fig\n", - "\n", - "# update plot callback function\n", - "def update_plot(change):\n", - " i_ax.cla()\n", - " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", - " i_ax_new.grid()\n", - " i_fig.canvas.draw()\n", - " i_fig.canvas.flush_events()\n", - "\n", - "dropdown.observe(update_plot, names='value')\n", - "\n", - "widgets.AppLayout(\n", - " header=dropdown,\n", - " center=i_fig.canvas,\n", - " pane_heights=[1, 6,1]\n", - ")\n" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.16" - }, - "widgets": { - "application/vnd.jupyter.widget-state+json": { - "state": {}, - "version_major": 2, - "version_minor": 0 - } - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/VAPs/quicklook/MFRSRCLDOD/.ipynb_checkpoints/mfrsrcldod1min.c1-checkpoint.ipynb b/VAPs/quicklook/MFRSRCLDOD/.ipynb_checkpoints/mfrsrcldod1min.c1-checkpoint.ipynb deleted file mode 100644 index 41b70061..00000000 --- a/VAPs/quicklook/MFRSRCLDOD/.ipynb_checkpoints/mfrsrcldod1min.c1-checkpoint.ipynb +++ /dev/null @@ -1,799 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "70840257-70e4-45e2-b491-14bff5a257a3", - "metadata": {}, - "source": [ - "# MFRSRCLDOD1MIN.C1 Plots\n", - "\n", - "[Click here](https://www.arm.gov/capabilities/vaps/mfrsrcldod) for more information about this vap." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "460fd89f-e034-452c-b837-f65c5958264f", - "metadata": {}, - "outputs": [], - "source": [ - "%matplotlib widget\n", - "import ipywidgets as widgets\n", - "\n", - "import matplotlib.pyplot as plt\n", - "import ipywidgets as widgets\n", - "import numpy as np\n", - "import pandas as pd\n", - "import os\n", - "from datetime import datetime\n", - "\n", - "import act\n", - "import xarray as xr\n", - "\n", - "# Data archive directory\n", - "DATA_DIR = r'/data/archive/'\n", - "\n", - "# Datastream info\n", - "DATASTREAM_NAME = 'mfrsrcldod1min'\n", - "DATA_LEVEL = 'c1'\n", - "LOCATIONS = [{'end_date': '2017-11-01', 'facility': 'M1', 'site': 'asi', 'start_date': '2016-05-02'}, {'end_date': '2019-04-30', 'facility': 'M1', 'site': 'cor', 'start_date': '2018-09-18'}, {'end_date': '2013-07-01', 'facility': 'M1', 'site': 'pvc', 'start_date': '2012-07-09'}, {'end_date': '2015-08-29', 'facility': 'M1', 'site': 'mao', 'start_date': '2015-04-17'}, {'end_date': '2012-04-01', 'facility': 'M1', 'site': 'pgh', 'start_date': '2011-07-19'}, {'end_date': '2012-02-05', 'facility': 'M1', 'site': 'gan', 'start_date': '2011-10-09'}, {'end_date': '2010-12-29', 'facility': 'M1', 'site': 'grw', 'start_date': '2009-05-04'}, {'end_date': '2018-03-13', 'facility': 'S1', 'site': 'mcq', 'start_date': '2016-04-01'}, {'end_date': '2007-11-15', 'facility': 'M1', 'site': 'fkb', 'start_date': '2007-05-07'}, {'end_date': '2019-10-27', 'facility': 'C1', 'site': 'ena', 'start_date': '2014-06-01'}, {'end_date': '2005-09-15', 'facility': 'M1', 'site': 'pye', 'start_date': '2005-02-19'}, {'end_date': '2021-01-29', 'facility': 'C1', 'site': 'sgp', 'start_date': '1998-01-01'}, {'end_date': '2011-10-19', 'facility': 'E10', 'site': 'sgp', 'start_date': '1997-10-31'}, {'end_date': '2021-09-21', 'facility': 'E11', 'site': 'sgp', 'start_date': '1997-08-23'}, {'end_date': '2021-09-21', 'facility': 'E12', 'site': 'sgp', 'start_date': '2001-07-24'}, {'end_date': '2022-06-30', 'facility': 'E13', 'site': 'sgp', 'start_date': '1998-07-10'}, {'end_date': '2021-09-21', 'facility': 'E15', 'site': 'sgp', 'start_date': '1997-09-10'}, {'end_date': '2011-11-15', 'facility': 'E16', 'site': 'sgp', 'start_date': '1997-08-21'}, {'end_date': '2009-11-17', 'facility': 'E18', 'site': 'sgp', 'start_date': '1997-10-17'}, {'end_date': '2011-05-23', 'facility': 'E19', 'site': 'sgp', 'start_date': '1998-07-09'}, {'end_date': '2009-10-14', 'facility': 'E1', 'site': 'sgp', 'start_date': '1997-11-21'}, {'end_date': '2011-11-17', 'facility': 'E20', 'site': 'sgp', 'start_date': '1999-04-24'}, {'end_date': '2009-12-01', 'facility': 'E22', 'site': 'sgp', 'start_date': '1999-01-15'}, {'end_date': '2009-11-14', 'facility': 'E24', 'site': 'sgp', 'start_date': '1997-11-26'}, {'end_date': '2002-04-08', 'facility': 'E25', 'site': 'sgp', 'start_date': '1998-01-11'}, {'end_date': '2009-12-04', 'facility': 'E27', 'site': 'sgp', 'start_date': '2003-12-30'}, {'end_date': '2009-10-20', 'facility': 'E2', 'site': 'sgp', 'start_date': '1997-11-05'}, {'end_date': '2021-06-22', 'facility': 'E31', 'site': 'sgp', 'start_date': '2011-11-26'}, {'end_date': '2021-09-21', 'facility': 'E32', 'site': 'sgp', 'start_date': '2011-11-26'}, {'end_date': '2021-09-21', 'facility': 'E33', 'site': 'sgp', 'start_date': '2011-09-27'}, {'end_date': '2021-09-21', 'facility': 'E34', 'site': 'sgp', 'start_date': '2011-09-28'}, {'end_date': '2021-09-21', 'facility': 'E35', 'site': 'sgp', 'start_date': '2011-09-28'}, {'end_date': '2021-09-21', 'facility': 'E36', 'site': 'sgp', 'start_date': '2011-10-18'}, {'end_date': '2021-09-21', 'facility': 'E37', 'site': 'sgp', 'start_date': '2011-11-02'}, {'end_date': '2017-10-15', 'facility': 'E38', 'site': 'sgp', 'start_date': '2011-12-15'}, {'end_date': '2009-10-28', 'facility': 'E3', 'site': 'sgp', 'start_date': '1998-07-24'}, {'end_date': '2011-09-26', 'facility': 'E4', 'site': 'sgp', 'start_date': '1997-12-20'}, {'end_date': '2009-11-02', 'facility': 'E5', 'site': 'sgp', 'start_date': '1998-03-22'}, {'end_date': '2011-10-18', 'facility': 'E6', 'site': 'sgp', 'start_date': '2003-12-19'}, {'end_date': '2011-11-14', 'facility': 'E7', 'site': 'sgp', 'start_date': '1999-07-12'}, {'end_date': '2009-11-10', 'facility': 'E8', 'site': 'sgp', 'start_date': '1997-09-03'}, {'end_date': '2021-09-21', 'facility': 'E9', 'site': 'sgp', 'start_date': '2008-03-25'}, {'end_date': '2014-06-04', 'facility': 'C1', 'site': 'twp', 'start_date': '1999-10-22'}, {'end_date': '2013-09-09', 'facility': 'C2', 'site': 'twp', 'start_date': '1999-09-08'}, {'end_date': '2014-10-05', 'facility': 'C3', 'site': 'twp', 'start_date': '2002-03-07'}]" - ] - }, - { - "cell_type": "markdown", - "id": "9faaf875", - "metadata": {}, - "source": [ - "## Define site, facility, and date range" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "ac6764f5", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "The following locations and date ranges are available for this VAP:\n" - ] - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
sitefacilitystart_dateend_date
0asiM12016-05-022017-11-01
1corM12018-09-182019-04-30
2pvcM12012-07-092013-07-01
3maoM12015-04-172015-08-29
4pghM12011-07-192012-04-01
5ganM12011-10-092012-02-05
6grwM12009-05-042010-12-29
7mcqS12016-04-012018-03-13
8fkbM12007-05-072007-11-15
9enaC12014-06-012019-10-27
10pyeM12005-02-192005-09-15
11sgpC11998-01-012021-01-29
12sgpE101997-10-312011-10-19
13sgpE111997-08-232021-09-21
14sgpE122001-07-242021-09-21
15sgpE131998-07-102022-06-30
16sgpE151997-09-102021-09-21
17sgpE161997-08-212011-11-15
18sgpE181997-10-172009-11-17
19sgpE191998-07-092011-05-23
20sgpE11997-11-212009-10-14
21sgpE201999-04-242011-11-17
22sgpE221999-01-152009-12-01
23sgpE241997-11-262009-11-14
24sgpE251998-01-112002-04-08
25sgpE272003-12-302009-12-04
26sgpE21997-11-052009-10-20
27sgpE312011-11-262021-06-22
28sgpE322011-11-262021-09-21
29sgpE332011-09-272021-09-21
30sgpE342011-09-282021-09-21
31sgpE352011-09-282021-09-21
32sgpE362011-10-182021-09-21
33sgpE372011-11-022021-09-21
34sgpE382011-12-152017-10-15
35sgpE31998-07-242009-10-28
36sgpE41997-12-202011-09-26
37sgpE51998-03-222009-11-02
38sgpE62003-12-192011-10-18
39sgpE71999-07-122011-11-14
40sgpE81997-09-032009-11-10
41sgpE92008-03-252021-09-21
42twpC11999-10-222014-06-04
43twpC21999-09-082013-09-09
44twpC32002-03-072014-10-05
\n", - "
" - ], - "text/plain": [ - " site facility start_date end_date\n", - "0 asi M1 2016-05-02 2017-11-01\n", - "1 cor M1 2018-09-18 2019-04-30\n", - "2 pvc M1 2012-07-09 2013-07-01\n", - "3 mao M1 2015-04-17 2015-08-29\n", - "4 pgh M1 2011-07-19 2012-04-01\n", - "5 gan M1 2011-10-09 2012-02-05\n", - "6 grw M1 2009-05-04 2010-12-29\n", - "7 mcq S1 2016-04-01 2018-03-13\n", - "8 fkb M1 2007-05-07 2007-11-15\n", - "9 ena C1 2014-06-01 2019-10-27\n", - "10 pye M1 2005-02-19 2005-09-15\n", - "11 sgp C1 1998-01-01 2021-01-29\n", - "12 sgp E10 1997-10-31 2011-10-19\n", - "13 sgp E11 1997-08-23 2021-09-21\n", - "14 sgp E12 2001-07-24 2021-09-21\n", - "15 sgp E13 1998-07-10 2022-06-30\n", - "16 sgp E15 1997-09-10 2021-09-21\n", - "17 sgp E16 1997-08-21 2011-11-15\n", - "18 sgp E18 1997-10-17 2009-11-17\n", - "19 sgp E19 1998-07-09 2011-05-23\n", - "20 sgp E1 1997-11-21 2009-10-14\n", - "21 sgp E20 1999-04-24 2011-11-17\n", - "22 sgp E22 1999-01-15 2009-12-01\n", - "23 sgp E24 1997-11-26 2009-11-14\n", - "24 sgp E25 1998-01-11 2002-04-08\n", - "25 sgp E27 2003-12-30 2009-12-04\n", - "26 sgp E2 1997-11-05 2009-10-20\n", - "27 sgp E31 2011-11-26 2021-06-22\n", - "28 sgp E32 2011-11-26 2021-09-21\n", - "29 sgp E33 2011-09-27 2021-09-21\n", - "30 sgp E34 2011-09-28 2021-09-21\n", - "31 sgp E35 2011-09-28 2021-09-21\n", - "32 sgp E36 2011-10-18 2021-09-21\n", - "33 sgp E37 2011-11-02 2021-09-21\n", - "34 sgp E38 2011-12-15 2017-10-15\n", - "35 sgp E3 1998-07-24 2009-10-28\n", - "36 sgp E4 1997-12-20 2011-09-26\n", - "37 sgp E5 1998-03-22 2009-11-02\n", - "38 sgp E6 2003-12-19 2011-10-18\n", - "39 sgp E7 1999-07-12 2011-11-14\n", - "40 sgp E8 1997-09-03 2009-11-10\n", - "41 sgp E9 2008-03-25 2021-09-21\n", - "42 twp C1 1999-10-22 2014-06-04\n", - "43 twp C2 1999-09-08 2013-09-09\n", - "44 twp C3 2002-03-07 2014-10-05" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "print(\"The following locations and date ranges are available for this VAP:\")\n", - "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" - ] - }, - { - "cell_type": "markdown", - "id": "8d132223", - "metadata": {}, - "source": [ - "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "e563983a", - "metadata": {}, - "outputs": [], - "source": [ - "site_facility = ( 'sgp', 'C1' )\n", - "\n", - "date_start = '2021-01-27'\n", - "date_end = '2021-01-29'" - ] - }, - { - "cell_type": "markdown", - "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", - "metadata": {}, - "source": [ - "## Load data files\n", - "Load data files from /data/archive/" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'/data/archive/sgp/sgpmfrsrcldod1minC1.c1'" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Compile list of files\n", - "site, facility = site_facility\n", - "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", - "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", - "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", - "dir_path\n" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "6be8f3dc", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "['20210127', '20210128', '20210129']" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "from datetime import date, timedelta\n", - "import pandas as pd\n", - "\n", - "def get_ARM_formated_dates(start_date, end_date):\n", - " \"\"\"\n", - " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", - " EXAMPLE:\n", - " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", - " >> [\"20180219\", \"20180220\", \"20180221\"] \n", - " \"\"\"\n", - " \n", - " _start_date = pd.to_datetime(start_date)\n", - " _end_date = pd.to_datetime(end_date)\n", - " \n", - " delta = _end_date - _start_date # returns timedelta \n", - " dates = []\n", - "\n", - " for i in range(delta.days + 1):\n", - " day = _start_date + timedelta(days=i)\n", - " day_formated = day.strftime(format=\"%Y%m%d\")\n", - " dates.append(day_formated)\n", - " return dates\n", - "\n", - "\n", - "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "51feea2e", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "['/data/archive/sgp/sgpmfrsrcldod1minC1.c1/sgpmfrsrcldod1minC1.c1.20210127.000000.cdf',\n", - " '/data/archive/sgp/sgpmfrsrcldod1minC1.c1/sgpmfrsrcldod1minC1.c1.20210128.000000.cdf',\n", - " '/data/archive/sgp/sgpmfrsrcldod1minC1.c1/sgpmfrsrcldod1minC1.c1.20210129.000000.cdf']" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Filter a list of files based on date pattern\n", - "import glob\n", - "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", - "files_filter = []\n", - "for date in dates:\n", - " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", - " files_filter\n", - "files_filter" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "id": "b0e5d0d7", - "metadata": {}, - "outputs": [ - { - "ename": "ValueError", - "evalue": "cannot reindex or align along dimension 'n_Io' because of conflicting dimension sizes: {113, 114}", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", - "Cell \u001b[0;32mIn[7], line 3\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[38;5;66;03m# Load files as a single dataset\u001b[39;00m\n\u001b[1;32m 2\u001b[0m files_list \u001b[38;5;241m=\u001b[39m files_filter \n\u001b[0;32m----> 3\u001b[0m ds \u001b[38;5;241m=\u001b[39m \u001b[43mact\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mio\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43marmfiles\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mread_netcdf\u001b[49m\u001b[43m(\u001b[49m\u001b[43mfiles_list\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 4\u001b[0m ds\u001b[38;5;241m.\u001b[39mclean\u001b[38;5;241m.\u001b[39mcleanup()\n\u001b[1;32m 5\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;132;01m{\u001b[39;00m\u001b[38;5;28mlen\u001b[39m(files_list)\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m files loaded\u001b[39m\u001b[38;5;124m'\u001b[39m)\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/act/io/armfiles.py:168\u001b[0m, in \u001b[0;36mread_netcdf\u001b[0;34m(filenames, concat_dim, return_None, combine, decode_times, use_cftime, use_base_time, combine_attrs, cleanup_qc, keep_variables, **kwargs)\u001b[0m\n\u001b[1;32m 164\u001b[0m ds \u001b[38;5;241m=\u001b[39m xr\u001b[38;5;241m.\u001b[39mopen_mfdataset(filenames, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs)\n\u001b[1;32m 166\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m 167\u001b[0m \u001b[38;5;66;03m# When all else fails raise the orginal exception\u001b[39;00m\n\u001b[0;32m--> 168\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m exception\n\u001b[1;32m 170\u001b[0m \u001b[38;5;66;03m# If requested use base_time and time_offset to derive time. Assumes that the units\u001b[39;00m\n\u001b[1;32m 171\u001b[0m \u001b[38;5;66;03m# of both are in seconds and that the value is number of seconds since epoch.\u001b[39;00m\n\u001b[1;32m 172\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m use_base_time:\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/act/io/armfiles.py:143\u001b[0m, in \u001b[0;36mread_netcdf\u001b[0;34m(filenames, concat_dim, return_None, combine, decode_times, use_cftime, use_base_time, combine_attrs, cleanup_qc, keep_variables, **kwargs)\u001b[0m\n\u001b[1;32m 139\u001b[0m except_tuple \u001b[38;5;241m=\u001b[39m except_tuple \u001b[38;5;241m+\u001b[39m (\u001b[38;5;167;01mFileNotFoundError\u001b[39;00m, \u001b[38;5;167;01mOSError\u001b[39;00m)\n\u001b[1;32m 141\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[1;32m 142\u001b[0m \u001b[38;5;66;03m# Read data file with Xarray function\u001b[39;00m\n\u001b[0;32m--> 143\u001b[0m ds \u001b[38;5;241m=\u001b[39m \u001b[43mxr\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mopen_mfdataset\u001b[49m\u001b[43m(\u001b[49m\u001b[43mfilenames\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 145\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m except_tuple \u001b[38;5;28;01mas\u001b[39;00m exception:\n\u001b[1;32m 146\u001b[0m \u001b[38;5;66;03m# If requested return None for File not found error\u001b[39;00m\n\u001b[1;32m 147\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mtype\u001b[39m(exception)\u001b[38;5;241m.\u001b[39m\u001b[38;5;18m__name__\u001b[39m \u001b[38;5;241m==\u001b[39m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mFileNotFoundError\u001b[39m\u001b[38;5;124m'\u001b[39m:\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/backends/api.py:1026\u001b[0m, in \u001b[0;36mopen_mfdataset\u001b[0;34m(paths, chunks, concat_dim, compat, preprocess, engine, data_vars, coords, combine, parallel, join, attrs_file, combine_attrs, **kwargs)\u001b[0m\n\u001b[1;32m 1013\u001b[0m combined \u001b[38;5;241m=\u001b[39m _nested_combine(\n\u001b[1;32m 1014\u001b[0m datasets,\n\u001b[1;32m 1015\u001b[0m concat_dims\u001b[38;5;241m=\u001b[39mconcat_dim,\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 1021\u001b[0m combine_attrs\u001b[38;5;241m=\u001b[39mcombine_attrs,\n\u001b[1;32m 1022\u001b[0m )\n\u001b[1;32m 1023\u001b[0m \u001b[38;5;28;01melif\u001b[39;00m combine \u001b[38;5;241m==\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mby_coords\u001b[39m\u001b[38;5;124m\"\u001b[39m:\n\u001b[1;32m 1024\u001b[0m \u001b[38;5;66;03m# Redo ordering from coordinates, ignoring how they were ordered\u001b[39;00m\n\u001b[1;32m 1025\u001b[0m \u001b[38;5;66;03m# previously\u001b[39;00m\n\u001b[0;32m-> 1026\u001b[0m combined \u001b[38;5;241m=\u001b[39m \u001b[43mcombine_by_coords\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 1027\u001b[0m \u001b[43m \u001b[49m\u001b[43mdatasets\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1028\u001b[0m \u001b[43m \u001b[49m\u001b[43mcompat\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcompat\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1029\u001b[0m \u001b[43m \u001b[49m\u001b[43mdata_vars\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mdata_vars\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1030\u001b[0m \u001b[43m \u001b[49m\u001b[43mcoords\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcoords\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1031\u001b[0m \u001b[43m \u001b[49m\u001b[43mjoin\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mjoin\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1032\u001b[0m \u001b[43m \u001b[49m\u001b[43mcombine_attrs\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcombine_attrs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1033\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 1034\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m 1035\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\n\u001b[1;32m 1036\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;132;01m{}\u001b[39;00m\u001b[38;5;124m is an invalid option for the keyword argument\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 1037\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m ``combine``\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;241m.\u001b[39mformat(combine)\n\u001b[1;32m 1038\u001b[0m )\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/core/combine.py:982\u001b[0m, in \u001b[0;36mcombine_by_coords\u001b[0;34m(data_objects, compat, data_vars, coords, fill_value, join, combine_attrs, datasets)\u001b[0m\n\u001b[1;32m 980\u001b[0m concatenated_grouped_by_data_vars \u001b[38;5;241m=\u001b[39m []\n\u001b[1;32m 981\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m \u001b[38;5;28mvars\u001b[39m, datasets_with_same_vars \u001b[38;5;129;01min\u001b[39;00m grouped_by_vars:\n\u001b[0;32m--> 982\u001b[0m concatenated \u001b[38;5;241m=\u001b[39m \u001b[43m_combine_single_variable_hypercube\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 983\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43mlist\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mdatasets_with_same_vars\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 984\u001b[0m \u001b[43m \u001b[49m\u001b[43mfill_value\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mfill_value\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 985\u001b[0m \u001b[43m \u001b[49m\u001b[43mdata_vars\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mdata_vars\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 986\u001b[0m \u001b[43m \u001b[49m\u001b[43mcoords\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcoords\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 987\u001b[0m \u001b[43m \u001b[49m\u001b[43mcompat\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcompat\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 988\u001b[0m \u001b[43m \u001b[49m\u001b[43mjoin\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mjoin\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 989\u001b[0m \u001b[43m \u001b[49m\u001b[43mcombine_attrs\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcombine_attrs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 990\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 991\u001b[0m concatenated_grouped_by_data_vars\u001b[38;5;241m.\u001b[39mappend(concatenated)\n\u001b[1;32m 993\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m merge(\n\u001b[1;32m 994\u001b[0m concatenated_grouped_by_data_vars,\n\u001b[1;32m 995\u001b[0m compat\u001b[38;5;241m=\u001b[39mcompat,\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 998\u001b[0m combine_attrs\u001b[38;5;241m=\u001b[39mcombine_attrs,\n\u001b[1;32m 999\u001b[0m )\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/core/combine.py:640\u001b[0m, in \u001b[0;36m_combine_single_variable_hypercube\u001b[0;34m(datasets, fill_value, data_vars, coords, compat, join, combine_attrs)\u001b[0m\n\u001b[1;32m 637\u001b[0m _check_dimension_depth_tile_ids(combined_ids)\n\u001b[1;32m 639\u001b[0m \u001b[38;5;66;03m# Concatenate along all of concat_dims one by one to create single ds\u001b[39;00m\n\u001b[0;32m--> 640\u001b[0m concatenated \u001b[38;5;241m=\u001b[39m \u001b[43m_combine_nd\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 641\u001b[0m \u001b[43m \u001b[49m\u001b[43mcombined_ids\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 642\u001b[0m \u001b[43m \u001b[49m\u001b[43mconcat_dims\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mconcat_dims\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 643\u001b[0m \u001b[43m \u001b[49m\u001b[43mdata_vars\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mdata_vars\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 644\u001b[0m \u001b[43m \u001b[49m\u001b[43mcoords\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcoords\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 645\u001b[0m \u001b[43m \u001b[49m\u001b[43mcompat\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcompat\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 646\u001b[0m \u001b[43m \u001b[49m\u001b[43mfill_value\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mfill_value\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 647\u001b[0m \u001b[43m \u001b[49m\u001b[43mjoin\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mjoin\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 648\u001b[0m \u001b[43m \u001b[49m\u001b[43mcombine_attrs\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcombine_attrs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 649\u001b[0m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 651\u001b[0m \u001b[38;5;66;03m# Check the overall coordinates are monotonically increasing\u001b[39;00m\n\u001b[1;32m 652\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m dim \u001b[38;5;129;01min\u001b[39;00m concat_dims:\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/core/combine.py:239\u001b[0m, in \u001b[0;36m_combine_nd\u001b[0;34m(combined_ids, concat_dims, data_vars, coords, compat, fill_value, join, combine_attrs)\u001b[0m\n\u001b[1;32m 235\u001b[0m \u001b[38;5;66;03m# Each iteration of this loop reduces the length of the tile_ids tuples\u001b[39;00m\n\u001b[1;32m 236\u001b[0m \u001b[38;5;66;03m# by one. It always combines along the first dimension, removing the first\u001b[39;00m\n\u001b[1;32m 237\u001b[0m \u001b[38;5;66;03m# element of the tuple\u001b[39;00m\n\u001b[1;32m 238\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m concat_dim \u001b[38;5;129;01min\u001b[39;00m concat_dims:\n\u001b[0;32m--> 239\u001b[0m combined_ids \u001b[38;5;241m=\u001b[39m \u001b[43m_combine_all_along_first_dim\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 240\u001b[0m \u001b[43m \u001b[49m\u001b[43mcombined_ids\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 241\u001b[0m \u001b[43m \u001b[49m\u001b[43mdim\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mconcat_dim\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 242\u001b[0m \u001b[43m \u001b[49m\u001b[43mdata_vars\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mdata_vars\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 243\u001b[0m \u001b[43m \u001b[49m\u001b[43mcoords\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcoords\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 244\u001b[0m \u001b[43m \u001b[49m\u001b[43mcompat\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcompat\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 245\u001b[0m \u001b[43m \u001b[49m\u001b[43mfill_value\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mfill_value\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 246\u001b[0m \u001b[43m \u001b[49m\u001b[43mjoin\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mjoin\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 247\u001b[0m \u001b[43m \u001b[49m\u001b[43mcombine_attrs\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcombine_attrs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 248\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 249\u001b[0m (combined_ds,) \u001b[38;5;241m=\u001b[39m combined_ids\u001b[38;5;241m.\u001b[39mvalues()\n\u001b[1;32m 250\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m combined_ds\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/core/combine.py:275\u001b[0m, in \u001b[0;36m_combine_all_along_first_dim\u001b[0;34m(combined_ids, dim, data_vars, coords, compat, fill_value, join, combine_attrs)\u001b[0m\n\u001b[1;32m 273\u001b[0m combined_ids \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mdict\u001b[39m(\u001b[38;5;28msorted\u001b[39m(group))\n\u001b[1;32m 274\u001b[0m datasets \u001b[38;5;241m=\u001b[39m combined_ids\u001b[38;5;241m.\u001b[39mvalues()\n\u001b[0;32m--> 275\u001b[0m new_combined_ids[new_id] \u001b[38;5;241m=\u001b[39m \u001b[43m_combine_1d\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 276\u001b[0m \u001b[43m \u001b[49m\u001b[43mdatasets\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mdim\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mcompat\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mdata_vars\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mcoords\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mfill_value\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mjoin\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mcombine_attrs\u001b[49m\n\u001b[1;32m 277\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 278\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m new_combined_ids\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/core/combine.py:298\u001b[0m, in \u001b[0;36m_combine_1d\u001b[0;34m(datasets, concat_dim, compat, data_vars, coords, fill_value, join, combine_attrs)\u001b[0m\n\u001b[1;32m 296\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m concat_dim \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[1;32m 297\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m--> 298\u001b[0m combined \u001b[38;5;241m=\u001b[39m \u001b[43mconcat\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 299\u001b[0m \u001b[43m \u001b[49m\u001b[43mdatasets\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 300\u001b[0m \u001b[43m \u001b[49m\u001b[43mdim\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mconcat_dim\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 301\u001b[0m \u001b[43m \u001b[49m\u001b[43mdata_vars\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mdata_vars\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 302\u001b[0m \u001b[43m \u001b[49m\u001b[43mcoords\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcoords\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 303\u001b[0m \u001b[43m \u001b[49m\u001b[43mcompat\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcompat\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 304\u001b[0m \u001b[43m \u001b[49m\u001b[43mfill_value\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mfill_value\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 305\u001b[0m \u001b[43m \u001b[49m\u001b[43mjoin\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mjoin\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 306\u001b[0m \u001b[43m \u001b[49m\u001b[43mcombine_attrs\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcombine_attrs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 307\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 308\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m err:\n\u001b[1;32m 309\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mencountered unexpected variable\u001b[39m\u001b[38;5;124m\"\u001b[39m \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mstr\u001b[39m(err):\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/core/concat.py:248\u001b[0m, in \u001b[0;36mconcat\u001b[0;34m(objs, dim, data_vars, coords, compat, positions, fill_value, join, combine_attrs)\u001b[0m\n\u001b[1;32m 236\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m _dataarray_concat(\n\u001b[1;32m 237\u001b[0m objs,\n\u001b[1;32m 238\u001b[0m dim\u001b[38;5;241m=\u001b[39mdim,\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 245\u001b[0m combine_attrs\u001b[38;5;241m=\u001b[39mcombine_attrs,\n\u001b[1;32m 246\u001b[0m )\n\u001b[1;32m 247\u001b[0m \u001b[38;5;28;01melif\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(first_obj, Dataset):\n\u001b[0;32m--> 248\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43m_dataset_concat\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 249\u001b[0m \u001b[43m \u001b[49m\u001b[43mobjs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 250\u001b[0m \u001b[43m \u001b[49m\u001b[43mdim\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mdim\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 251\u001b[0m \u001b[43m \u001b[49m\u001b[43mdata_vars\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mdata_vars\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 252\u001b[0m \u001b[43m \u001b[49m\u001b[43mcoords\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcoords\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 253\u001b[0m \u001b[43m \u001b[49m\u001b[43mcompat\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcompat\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 254\u001b[0m \u001b[43m \u001b[49m\u001b[43mpositions\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mpositions\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 255\u001b[0m \u001b[43m \u001b[49m\u001b[43mfill_value\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mfill_value\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 256\u001b[0m \u001b[43m \u001b[49m\u001b[43mjoin\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mjoin\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 257\u001b[0m \u001b[43m \u001b[49m\u001b[43mcombine_attrs\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcombine_attrs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 258\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 259\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m 260\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mTypeError\u001b[39;00m(\n\u001b[1;32m 261\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mcan only concatenate xarray Dataset and DataArray \u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 262\u001b[0m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mobjects, got \u001b[39m\u001b[38;5;132;01m{\u001b[39;00m\u001b[38;5;28mtype\u001b[39m(first_obj)\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 263\u001b[0m )\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/core/concat.py:471\u001b[0m, in \u001b[0;36m_dataset_concat\u001b[0;34m(datasets, dim, data_vars, coords, compat, positions, fill_value, join, combine_attrs)\u001b[0m\n\u001b[1;32m 468\u001b[0m \u001b[38;5;66;03m# Make sure we're working on a copy (we'll be loading variables)\u001b[39;00m\n\u001b[1;32m 469\u001b[0m datasets \u001b[38;5;241m=\u001b[39m [ds\u001b[38;5;241m.\u001b[39mcopy() \u001b[38;5;28;01mfor\u001b[39;00m ds \u001b[38;5;129;01min\u001b[39;00m datasets]\n\u001b[1;32m 470\u001b[0m datasets \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mlist\u001b[39m(\n\u001b[0;32m--> 471\u001b[0m \u001b[43malign\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mdatasets\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mjoin\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mjoin\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mcopy\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43;01mFalse\u001b[39;49;00m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mexclude\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43m[\u001b[49m\u001b[43mdim\u001b[49m\u001b[43m]\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mfill_value\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mfill_value\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 472\u001b[0m )\n\u001b[1;32m 474\u001b[0m dim_coords, dims_sizes, coord_names, data_names \u001b[38;5;241m=\u001b[39m _parse_datasets(datasets)\n\u001b[1;32m 475\u001b[0m dim_names \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mset\u001b[39m(dim_coords)\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/core/alignment.py:797\u001b[0m, in \u001b[0;36malign\u001b[0;34m(join, copy, indexes, exclude, fill_value, *objects)\u001b[0m\n\u001b[1;32m 601\u001b[0m \u001b[38;5;250m\u001b[39m\u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[1;32m 602\u001b[0m \u001b[38;5;124;03mGiven any number of Dataset and/or DataArray objects, returns new\u001b[39;00m\n\u001b[1;32m 603\u001b[0m \u001b[38;5;124;03mobjects with aligned indexes and dimension sizes.\u001b[39;00m\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 787\u001b[0m \n\u001b[1;32m 788\u001b[0m \u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[1;32m 789\u001b[0m aligner \u001b[38;5;241m=\u001b[39m Aligner(\n\u001b[1;32m 790\u001b[0m objects,\n\u001b[1;32m 791\u001b[0m join\u001b[38;5;241m=\u001b[39mjoin,\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 795\u001b[0m fill_value\u001b[38;5;241m=\u001b[39mfill_value,\n\u001b[1;32m 796\u001b[0m )\n\u001b[0;32m--> 797\u001b[0m \u001b[43maligner\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43malign\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 798\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m aligner\u001b[38;5;241m.\u001b[39mresults\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/core/alignment.py:585\u001b[0m, in \u001b[0;36mAligner.align\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 583\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39massert_no_index_conflict()\n\u001b[1;32m 584\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39malign_indexes()\n\u001b[0;32m--> 585\u001b[0m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43massert_unindexed_dim_sizes_equal\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 587\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mjoin \u001b[38;5;241m==\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124moverride\u001b[39m\u001b[38;5;124m\"\u001b[39m:\n\u001b[1;32m 588\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39moverride_indexes()\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/core/alignment.py:484\u001b[0m, in \u001b[0;36mAligner.assert_unindexed_dim_sizes_equal\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 482\u001b[0m add_err_msg \u001b[38;5;241m=\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 483\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mlen\u001b[39m(sizes) \u001b[38;5;241m>\u001b[39m \u001b[38;5;241m1\u001b[39m:\n\u001b[0;32m--> 484\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\n\u001b[1;32m 485\u001b[0m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mcannot reindex or align along dimension \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mdim\u001b[38;5;132;01m!r}\u001b[39;00m\u001b[38;5;124m \u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 486\u001b[0m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mbecause of conflicting dimension sizes: \u001b[39m\u001b[38;5;132;01m{\u001b[39;00msizes\u001b[38;5;132;01m!r}\u001b[39;00m\u001b[38;5;124m\"\u001b[39m \u001b[38;5;241m+\u001b[39m add_err_msg\n\u001b[1;32m 487\u001b[0m )\n", - "\u001b[0;31mValueError\u001b[0m: cannot reindex or align along dimension 'n_Io' because of conflicting dimension sizes: {113, 114}" - ] - } - ], - "source": [ - "# Load files as a single dataset\n", - "files_list = files_filter \n", - "ds = act.io.armfiles.read_netcdf(files_list)\n", - "ds.clean.cleanup()\n", - "print(f'{len(files_list)} files loaded')\n", - "ds\n" - ] - }, - { - "cell_type": "markdown", - "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", - "metadata": {}, - "source": [ - "## Plot time series data\n", - "#### Define the list of variables to be plotted:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d09b789e-84f1-4605-846b-a72c110c8048", - "metadata": {}, - "outputs": [], - "source": [ - "variables_to_plot = ['optical_depth_instantaneous']" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", - "metadata": {}, - "outputs": [], - "source": [ - "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", - "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", - "\n", - "for i,v in enumerate(variables_to_plot):\n", - " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", - " ts_ax.grid()\n", - "\n", - "plt.show()\n" - ] - }, - { - "cell_type": "markdown", - "id": "194399aa-1907-452b-8ba9-bc31d7f60291", - "metadata": {}, - "source": [ - "## Quality check plots\n", - "#### Define variable for QC plot" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", - "metadata": {}, - "outputs": [], - "source": [ - "qc_variable = 'optical_depth_instantaneous'" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", - "metadata": {}, - "outputs": [], - "source": [ - "# QC Plot\n", - "if ('qc_'+qc_variable) in ds.variables:\n", - "\n", - " # Plot\n", - " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", - " qc_display.add_subplots((2,), figsize = (9.5,10))\n", - " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", - " qc_ax.grid()\n", - " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", - "\n", - " plt.show()\n", - "else:\n", - " print(f'QC not available for the selected field: {qc_variable}')\n" - ] - }, - { - "cell_type": "markdown", - "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", - "metadata": {}, - "source": [ - "## Field selection dropdown menu\n", - "Select variable to be plotted from a dropdown menu" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", - "metadata": {}, - "outputs": [], - "source": [ - "plt.ioff()\n", - "\n", - "# populate dropdown menu with available variables \n", - "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", - "d_variable = 'optical_depth_instantaneous'\n", - "dropdown = widgets.Dropdown(\n", - " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", - " value= d_variable,\n", - " description='Field:',\n", - " disabled=False,\n", - ")\n", - "dropdown.layout.margin = '0px 30% 0px 20%'\n", - "dropdown.layout.width = '50%'\n", - "\n", - "# set up display\n", - "i_display = act.plotting.TimeSeriesDisplay(ds)\n", - "i_display.add_subplots((1,), figsize = (9.5,5))\n", - "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", - "i_ax.grid()\n", - "i_fig = i_display.fig\n", - "\n", - "# update plot callback function\n", - "def update_plot(change):\n", - " i_ax.cla()\n", - " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", - " i_ax_new.grid()\n", - " i_fig.canvas.draw()\n", - " i_fig.canvas.flush_events()\n", - "\n", - "dropdown.observe(update_plot, names='value')\n", - "\n", - "widgets.AppLayout(\n", - " header=dropdown,\n", - " center=i_fig.canvas,\n", - " pane_heights=[1, 6,1]\n", - ")\n" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.16" - }, - "widgets": { - "application/vnd.jupyter.widget-state+json": { - "state": {}, - "version_major": 2, - "version_minor": 0 - } - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/VAPs/quicklook/MICROBASE/.ipynb_checkpoints/microbasepi2.c1-checkpoint.ipynb b/VAPs/quicklook/MICROBASE/.ipynb_checkpoints/microbasepi2.c1-checkpoint.ipynb deleted file mode 100644 index 72c386b4..00000000 --- a/VAPs/quicklook/MICROBASE/.ipynb_checkpoints/microbasepi2.c1-checkpoint.ipynb +++ /dev/null @@ -1,468 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "70840257-70e4-45e2-b491-14bff5a257a3", - "metadata": {}, - "source": [ - "# MICROBASEPI2.C1 Plots\n", - "\n", - "[Click here](https://www.arm.gov/capabilities/vaps/microbase) for more information about this vap." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "460fd89f-e034-452c-b837-f65c5958264f", - "metadata": {}, - "outputs": [], - "source": [ - "%matplotlib widget\n", - "import ipywidgets as widgets\n", - "\n", - "import matplotlib.pyplot as plt\n", - "import ipywidgets as widgets\n", - "import numpy as np\n", - "import pandas as pd\n", - "import os\n", - "from datetime import datetime\n", - "\n", - "import act\n", - "import xarray as xr\n", - "\n", - "# Data archive directory\n", - "DATA_DIR = r'/data/archive/'\n", - "\n", - "# Datastream info\n", - "DATASTREAM_NAME = 'microbasepi2'\n", - "DATA_LEVEL = 'c1'\n", - "LOCATIONS = [{'end_date': '2011-03-22', 'facility': 'C1', 'site': 'nsa', 'start_date': '2002-01-01'}, {'end_date': '2010-12-30', 'facility': 'C1', 'site': 'sgp', 'start_date': '1996-11-08'}, {'end_date': '2011-02-25', 'facility': 'C1', 'site': 'twp', 'start_date': '1999-07-01'}, {'end_date': '2009-02-13', 'facility': 'C2', 'site': 'twp', 'start_date': '2002-01-01'}, {'end_date': '2011-02-27', 'facility': 'C3', 'site': 'twp', 'start_date': '2005-11-04'}]" - ] - }, - { - "cell_type": "markdown", - "id": "9faaf875", - "metadata": {}, - "source": [ - "## Define site, facility, and date range" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "ac6764f5", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "The following locations and date ranges are available for this VAP:\n" - ] - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
sitefacilitystart_dateend_date
0nsaC12002-01-012011-03-22
1sgpC11996-11-082010-12-30
2twpC11999-07-012011-02-25
3twpC22002-01-012009-02-13
4twpC32005-11-042011-02-27
\n", - "
" - ], - "text/plain": [ - " site facility start_date end_date\n", - "0 nsa C1 2002-01-01 2011-03-22\n", - "1 sgp C1 1996-11-08 2010-12-30\n", - "2 twp C1 1999-07-01 2011-02-25\n", - "3 twp C2 2002-01-01 2009-02-13\n", - "4 twp C3 2005-11-04 2011-02-27" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "print(\"The following locations and date ranges are available for this VAP:\")\n", - "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" - ] - }, - { - "cell_type": "markdown", - "id": "8d132223", - "metadata": {}, - "source": [ - "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "e563983a", - "metadata": {}, - "outputs": [], - "source": [ - "site_facility = ( 'sgp', 'C1' )\n", - "\n", - "date_start = '2010-12-28'\n", - "date_end = '2010-12-30'" - ] - }, - { - "cell_type": "markdown", - "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", - "metadata": {}, - "source": [ - "## Load data files\n", - "Load data files from /data/archive/" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'/data/archive/sgp/sgpmicrobasepi2C1.c1'" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Compile list of files\n", - "site, facility = site_facility\n", - "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", - "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", - "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", - "dir_path\n" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "6be8f3dc", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "['20101228', '20101229', '20101230']" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "from datetime import date, timedelta\n", - "import pandas as pd\n", - "\n", - "def get_ARM_formated_dates(start_date, end_date):\n", - " \"\"\"\n", - " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", - " EXAMPLE:\n", - " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", - " >> [\"20180219\", \"20180220\", \"20180221\"] \n", - " \"\"\"\n", - " \n", - " _start_date = pd.to_datetime(start_date)\n", - " _end_date = pd.to_datetime(end_date)\n", - " \n", - " delta = _end_date - _start_date # returns timedelta \n", - " dates = []\n", - "\n", - " for i in range(delta.days + 1):\n", - " day = _start_date + timedelta(days=i)\n", - " day_formated = day.strftime(format=\"%Y%m%d\")\n", - " dates.append(day_formated)\n", - " return dates\n", - "\n", - "\n", - "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "51feea2e", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "['/data/archive/sgp/sgpmicrobasepi2C1.c1/sgpmicrobasepi2C1.c1.20101228.000000.cdf',\n", - " '/data/archive/sgp/sgpmicrobasepi2C1.c1/sgpmicrobasepi2C1.c1.20101229.000000.cdf',\n", - " '/data/archive/sgp/sgpmicrobasepi2C1.c1/sgpmicrobasepi2C1.c1.20101230.000000.cdf']" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Filter a list of files based on date pattern\n", - "import glob\n", - "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", - "files_filter = []\n", - "for date in dates:\n", - " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", - " files_filter\n", - "files_filter" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "id": "2d9b10cd", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Requirement already satisfied: cftime in /home/kefeimo/.conda/envs/jupyter-vaps/lib/python3.8/site-packages (1.6.2)\n", - "Requirement already satisfied: numpy>1.13.3 in /home/kefeimo/.conda/envs/jupyter-vaps/lib/python3.8/site-packages (from cftime) (1.24.2)\n" - ] - } - ], - "source": [ - "! pip install cftime " - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "id": "b0e5d0d7", - "metadata": {}, - "outputs": [ - { - "ename": "ValueError", - "evalue": "Failed to decode variable 'time_offset': unable to decode time units 'seconds since base_time' with 'the default calendar'. Try opening your dataset with decode_times=False or installing cftime if it is not installed.", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/coding/times.py:184\u001b[0m, in \u001b[0;36m_decode_cf_datetime_dtype\u001b[0;34m(data, units, calendar, use_cftime)\u001b[0m\n\u001b[1;32m 183\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m--> 184\u001b[0m result \u001b[38;5;241m=\u001b[39m \u001b[43mdecode_cf_datetime\u001b[49m\u001b[43m(\u001b[49m\u001b[43mexample_value\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43munits\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mcalendar\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43muse_cftime\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 185\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mException\u001b[39;00m:\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/coding/times.py:308\u001b[0m, in \u001b[0;36mdecode_cf_datetime\u001b[0;34m(num_dates, units, calendar, use_cftime)\u001b[0m\n\u001b[1;32m 307\u001b[0m \u001b[38;5;28;01melif\u001b[39;00m use_cftime:\n\u001b[0;32m--> 308\u001b[0m dates \u001b[38;5;241m=\u001b[39m \u001b[43m_decode_datetime_with_cftime\u001b[49m\u001b[43m(\u001b[49m\u001b[43mflat_num_dates\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43munits\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mcalendar\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 309\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/coding/times.py:208\u001b[0m, in \u001b[0;36m_decode_datetime_with_cftime\u001b[0;34m(num_dates, units, calendar)\u001b[0m\n\u001b[1;32m 206\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m num_dates\u001b[38;5;241m.\u001b[39msize \u001b[38;5;241m>\u001b[39m \u001b[38;5;241m0\u001b[39m:\n\u001b[1;32m 207\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m np\u001b[38;5;241m.\u001b[39masarray(\n\u001b[0;32m--> 208\u001b[0m \u001b[43mcftime\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mnum2date\u001b[49m\u001b[43m(\u001b[49m\u001b[43mnum_dates\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43munits\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mcalendar\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43monly_use_cftime_datetimes\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43;01mTrue\u001b[39;49;00m\u001b[43m)\u001b[49m\n\u001b[1;32m 209\u001b[0m )\n\u001b[1;32m 210\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n", - "File \u001b[0;32msrc/cftime/_cftime.pyx:580\u001b[0m, in \u001b[0;36mcftime._cftime.num2date\u001b[0;34m()\u001b[0m\n", - "File \u001b[0;32msrc/cftime/_cftime.pyx:110\u001b[0m, in \u001b[0;36mcftime._cftime._dateparse\u001b[0;34m()\u001b[0m\n", - "File \u001b[0;32msrc/cftime/_cftime.pyx:767\u001b[0m, in \u001b[0;36mcftime._cftime._parse_date\u001b[0;34m()\u001b[0m\n", - "\u001b[0;31mValueError\u001b[0m: Unable to parse date string 'base_time'", - "\nDuring handling of the above exception, another exception occurred:\n", - "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/conventions.py:551\u001b[0m, in \u001b[0;36mdecode_cf_variables\u001b[0;34m(variables, attributes, concat_characters, mask_and_scale, decode_times, decode_coords, drop_variables, use_cftime, decode_timedelta)\u001b[0m\n\u001b[1;32m 550\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m--> 551\u001b[0m new_vars[k] \u001b[38;5;241m=\u001b[39m \u001b[43mdecode_cf_variable\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 552\u001b[0m \u001b[43m \u001b[49m\u001b[43mk\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 553\u001b[0m \u001b[43m \u001b[49m\u001b[43mv\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 554\u001b[0m \u001b[43m \u001b[49m\u001b[43mconcat_characters\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mconcat_characters\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 555\u001b[0m \u001b[43m \u001b[49m\u001b[43mmask_and_scale\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mmask_and_scale\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 556\u001b[0m \u001b[43m \u001b[49m\u001b[43mdecode_times\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mdecode_times\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 557\u001b[0m \u001b[43m \u001b[49m\u001b[43mstack_char_dim\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mstack_char_dim\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 558\u001b[0m \u001b[43m \u001b[49m\u001b[43muse_cftime\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43muse_cftime\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 559\u001b[0m \u001b[43m \u001b[49m\u001b[43mdecode_timedelta\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mdecode_timedelta\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 560\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 561\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mException\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m e:\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/conventions.py:397\u001b[0m, in \u001b[0;36mdecode_cf_variable\u001b[0;34m(name, var, concat_characters, mask_and_scale, decode_times, decode_endianness, stack_char_dim, use_cftime, decode_timedelta)\u001b[0m\n\u001b[1;32m 396\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m decode_times:\n\u001b[0;32m--> 397\u001b[0m var \u001b[38;5;241m=\u001b[39m \u001b[43mtimes\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mCFDatetimeCoder\u001b[49m\u001b[43m(\u001b[49m\u001b[43muse_cftime\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43muse_cftime\u001b[49m\u001b[43m)\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mdecode\u001b[49m\u001b[43m(\u001b[49m\u001b[43mvar\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mname\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mname\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 399\u001b[0m dimensions, data, attributes, encoding \u001b[38;5;241m=\u001b[39m variables\u001b[38;5;241m.\u001b[39munpack_for_decoding(var)\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/coding/times.py:716\u001b[0m, in \u001b[0;36mCFDatetimeCoder.decode\u001b[0;34m(self, variable, name)\u001b[0m\n\u001b[1;32m 715\u001b[0m calendar \u001b[38;5;241m=\u001b[39m pop_to(attrs, encoding, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mcalendar\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[0;32m--> 716\u001b[0m dtype \u001b[38;5;241m=\u001b[39m \u001b[43m_decode_cf_datetime_dtype\u001b[49m\u001b[43m(\u001b[49m\u001b[43mdata\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43munits\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mcalendar\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43muse_cftime\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 717\u001b[0m transform \u001b[38;5;241m=\u001b[39m partial(\n\u001b[1;32m 718\u001b[0m decode_cf_datetime,\n\u001b[1;32m 719\u001b[0m units\u001b[38;5;241m=\u001b[39munits,\n\u001b[1;32m 720\u001b[0m calendar\u001b[38;5;241m=\u001b[39mcalendar,\n\u001b[1;32m 721\u001b[0m use_cftime\u001b[38;5;241m=\u001b[39m\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39muse_cftime,\n\u001b[1;32m 722\u001b[0m )\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/coding/times.py:194\u001b[0m, in \u001b[0;36m_decode_cf_datetime_dtype\u001b[0;34m(data, units, calendar, use_cftime)\u001b[0m\n\u001b[1;32m 189\u001b[0m msg \u001b[38;5;241m=\u001b[39m (\n\u001b[1;32m 190\u001b[0m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124munable to decode time units \u001b[39m\u001b[38;5;132;01m{\u001b[39;00munits\u001b[38;5;132;01m!r}\u001b[39;00m\u001b[38;5;124m with \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mcalendar_msg\u001b[38;5;132;01m!r}\u001b[39;00m\u001b[38;5;124m. Try \u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 191\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mopening your dataset with decode_times=False or installing cftime \u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 192\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mif it is not installed.\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 193\u001b[0m )\n\u001b[0;32m--> 194\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(msg)\n\u001b[1;32m 195\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n", - "\u001b[0;31mValueError\u001b[0m: unable to decode time units 'seconds since base_time' with 'the default calendar'. Try opening your dataset with decode_times=False or installing cftime if it is not installed.", - "\nDuring handling of the above exception, another exception occurred:\n", - "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", - "Cell \u001b[0;32mIn[7], line 3\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[38;5;66;03m# Load files as a single dataset\u001b[39;00m\n\u001b[1;32m 2\u001b[0m files_list \u001b[38;5;241m=\u001b[39m files_filter \n\u001b[0;32m----> 3\u001b[0m ds \u001b[38;5;241m=\u001b[39m \u001b[43mact\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mio\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43marmfiles\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mread_netcdf\u001b[49m\u001b[43m(\u001b[49m\u001b[43mfiles_list\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 4\u001b[0m ds\u001b[38;5;241m.\u001b[39mclean\u001b[38;5;241m.\u001b[39mcleanup()\n\u001b[1;32m 5\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;132;01m{\u001b[39;00m\u001b[38;5;28mlen\u001b[39m(files_list)\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m files loaded\u001b[39m\u001b[38;5;124m'\u001b[39m)\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/act/io/armfiles.py:168\u001b[0m, in \u001b[0;36mread_netcdf\u001b[0;34m(filenames, concat_dim, return_None, combine, decode_times, use_cftime, use_base_time, combine_attrs, cleanup_qc, keep_variables, **kwargs)\u001b[0m\n\u001b[1;32m 164\u001b[0m ds \u001b[38;5;241m=\u001b[39m xr\u001b[38;5;241m.\u001b[39mopen_mfdataset(filenames, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs)\n\u001b[1;32m 166\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m 167\u001b[0m \u001b[38;5;66;03m# When all else fails raise the orginal exception\u001b[39;00m\n\u001b[0;32m--> 168\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m exception\n\u001b[1;32m 170\u001b[0m \u001b[38;5;66;03m# If requested use base_time and time_offset to derive time. Assumes that the units\u001b[39;00m\n\u001b[1;32m 171\u001b[0m \u001b[38;5;66;03m# of both are in seconds and that the value is number of seconds since epoch.\u001b[39;00m\n\u001b[1;32m 172\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m use_base_time:\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/act/io/armfiles.py:143\u001b[0m, in \u001b[0;36mread_netcdf\u001b[0;34m(filenames, concat_dim, return_None, combine, decode_times, use_cftime, use_base_time, combine_attrs, cleanup_qc, keep_variables, **kwargs)\u001b[0m\n\u001b[1;32m 139\u001b[0m except_tuple \u001b[38;5;241m=\u001b[39m except_tuple \u001b[38;5;241m+\u001b[39m (\u001b[38;5;167;01mFileNotFoundError\u001b[39;00m, \u001b[38;5;167;01mOSError\u001b[39;00m)\n\u001b[1;32m 141\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[1;32m 142\u001b[0m \u001b[38;5;66;03m# Read data file with Xarray function\u001b[39;00m\n\u001b[0;32m--> 143\u001b[0m ds \u001b[38;5;241m=\u001b[39m \u001b[43mxr\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mopen_mfdataset\u001b[49m\u001b[43m(\u001b[49m\u001b[43mfilenames\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 145\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m except_tuple \u001b[38;5;28;01mas\u001b[39;00m exception:\n\u001b[1;32m 146\u001b[0m \u001b[38;5;66;03m# If requested return None for File not found error\u001b[39;00m\n\u001b[1;32m 147\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mtype\u001b[39m(exception)\u001b[38;5;241m.\u001b[39m\u001b[38;5;18m__name__\u001b[39m \u001b[38;5;241m==\u001b[39m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mFileNotFoundError\u001b[39m\u001b[38;5;124m'\u001b[39m:\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/backends/api.py:998\u001b[0m, in \u001b[0;36mopen_mfdataset\u001b[0;34m(paths, chunks, concat_dim, compat, preprocess, engine, data_vars, coords, combine, parallel, join, attrs_file, combine_attrs, **kwargs)\u001b[0m\n\u001b[1;32m 995\u001b[0m open_ \u001b[38;5;241m=\u001b[39m open_dataset\n\u001b[1;32m 996\u001b[0m getattr_ \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mgetattr\u001b[39m\n\u001b[0;32m--> 998\u001b[0m datasets \u001b[38;5;241m=\u001b[39m [open_(p, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mopen_kwargs) \u001b[38;5;28;01mfor\u001b[39;00m p \u001b[38;5;129;01min\u001b[39;00m paths]\n\u001b[1;32m 999\u001b[0m closers \u001b[38;5;241m=\u001b[39m [getattr_(ds, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m_close\u001b[39m\u001b[38;5;124m\"\u001b[39m) \u001b[38;5;28;01mfor\u001b[39;00m ds \u001b[38;5;129;01min\u001b[39;00m datasets]\n\u001b[1;32m 1000\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m preprocess \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/backends/api.py:998\u001b[0m, in \u001b[0;36m\u001b[0;34m(.0)\u001b[0m\n\u001b[1;32m 995\u001b[0m open_ \u001b[38;5;241m=\u001b[39m open_dataset\n\u001b[1;32m 996\u001b[0m getattr_ \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mgetattr\u001b[39m\n\u001b[0;32m--> 998\u001b[0m datasets \u001b[38;5;241m=\u001b[39m [\u001b[43mopen_\u001b[49m\u001b[43m(\u001b[49m\u001b[43mp\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mopen_kwargs\u001b[49m\u001b[43m)\u001b[49m \u001b[38;5;28;01mfor\u001b[39;00m p \u001b[38;5;129;01min\u001b[39;00m paths]\n\u001b[1;32m 999\u001b[0m closers \u001b[38;5;241m=\u001b[39m [getattr_(ds, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m_close\u001b[39m\u001b[38;5;124m\"\u001b[39m) \u001b[38;5;28;01mfor\u001b[39;00m ds \u001b[38;5;129;01min\u001b[39;00m datasets]\n\u001b[1;32m 1000\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m preprocess \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/backends/api.py:541\u001b[0m, in \u001b[0;36mopen_dataset\u001b[0;34m(filename_or_obj, engine, chunks, cache, decode_cf, mask_and_scale, decode_times, decode_timedelta, use_cftime, concat_characters, decode_coords, drop_variables, inline_array, backend_kwargs, **kwargs)\u001b[0m\n\u001b[1;32m 529\u001b[0m decoders \u001b[38;5;241m=\u001b[39m _resolve_decoders_kwargs(\n\u001b[1;32m 530\u001b[0m decode_cf,\n\u001b[1;32m 531\u001b[0m open_backend_dataset_parameters\u001b[38;5;241m=\u001b[39mbackend\u001b[38;5;241m.\u001b[39mopen_dataset_parameters,\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 537\u001b[0m decode_coords\u001b[38;5;241m=\u001b[39mdecode_coords,\n\u001b[1;32m 538\u001b[0m )\n\u001b[1;32m 540\u001b[0m overwrite_encoded_chunks \u001b[38;5;241m=\u001b[39m kwargs\u001b[38;5;241m.\u001b[39mpop(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124moverwrite_encoded_chunks\u001b[39m\u001b[38;5;124m\"\u001b[39m, \u001b[38;5;28;01mNone\u001b[39;00m)\n\u001b[0;32m--> 541\u001b[0m backend_ds \u001b[38;5;241m=\u001b[39m \u001b[43mbackend\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mopen_dataset\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 542\u001b[0m \u001b[43m \u001b[49m\u001b[43mfilename_or_obj\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 543\u001b[0m \u001b[43m \u001b[49m\u001b[43mdrop_variables\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mdrop_variables\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 544\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mdecoders\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 545\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 546\u001b[0m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 547\u001b[0m ds \u001b[38;5;241m=\u001b[39m _dataset_from_backend_dataset(\n\u001b[1;32m 548\u001b[0m backend_ds,\n\u001b[1;32m 549\u001b[0m filename_or_obj,\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 557\u001b[0m \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs,\n\u001b[1;32m 558\u001b[0m )\n\u001b[1;32m 559\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m ds\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/backends/netCDF4_.py:592\u001b[0m, in \u001b[0;36mNetCDF4BackendEntrypoint.open_dataset\u001b[0;34m(self, filename_or_obj, mask_and_scale, decode_times, concat_characters, decode_coords, drop_variables, use_cftime, decode_timedelta, group, mode, format, clobber, diskless, persist, lock, autoclose)\u001b[0m\n\u001b[1;32m 590\u001b[0m store_entrypoint \u001b[38;5;241m=\u001b[39m StoreBackendEntrypoint()\n\u001b[1;32m 591\u001b[0m \u001b[38;5;28;01mwith\u001b[39;00m close_on_error(store):\n\u001b[0;32m--> 592\u001b[0m ds \u001b[38;5;241m=\u001b[39m \u001b[43mstore_entrypoint\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mopen_dataset\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 593\u001b[0m \u001b[43m \u001b[49m\u001b[43mstore\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 594\u001b[0m \u001b[43m \u001b[49m\u001b[43mmask_and_scale\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mmask_and_scale\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 595\u001b[0m \u001b[43m \u001b[49m\u001b[43mdecode_times\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mdecode_times\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 596\u001b[0m \u001b[43m \u001b[49m\u001b[43mconcat_characters\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mconcat_characters\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 597\u001b[0m \u001b[43m \u001b[49m\u001b[43mdecode_coords\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mdecode_coords\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 598\u001b[0m \u001b[43m \u001b[49m\u001b[43mdrop_variables\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mdrop_variables\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 599\u001b[0m \u001b[43m \u001b[49m\u001b[43muse_cftime\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43muse_cftime\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 600\u001b[0m \u001b[43m \u001b[49m\u001b[43mdecode_timedelta\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mdecode_timedelta\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 601\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 602\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m ds\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/backends/store.py:35\u001b[0m, in \u001b[0;36mStoreBackendEntrypoint.open_dataset\u001b[0;34m(self, store, mask_and_scale, decode_times, concat_characters, decode_coords, drop_variables, use_cftime, decode_timedelta)\u001b[0m\n\u001b[1;32m 32\u001b[0m \u001b[38;5;28mvars\u001b[39m, attrs \u001b[38;5;241m=\u001b[39m store\u001b[38;5;241m.\u001b[39mload()\n\u001b[1;32m 33\u001b[0m encoding \u001b[38;5;241m=\u001b[39m store\u001b[38;5;241m.\u001b[39mget_encoding()\n\u001b[0;32m---> 35\u001b[0m \u001b[38;5;28mvars\u001b[39m, attrs, coord_names \u001b[38;5;241m=\u001b[39m \u001b[43mconventions\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mdecode_cf_variables\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 36\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43mvars\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[1;32m 37\u001b[0m \u001b[43m \u001b[49m\u001b[43mattrs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 38\u001b[0m \u001b[43m \u001b[49m\u001b[43mmask_and_scale\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mmask_and_scale\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 39\u001b[0m \u001b[43m \u001b[49m\u001b[43mdecode_times\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mdecode_times\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 40\u001b[0m \u001b[43m \u001b[49m\u001b[43mconcat_characters\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mconcat_characters\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 41\u001b[0m \u001b[43m \u001b[49m\u001b[43mdecode_coords\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mdecode_coords\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 42\u001b[0m \u001b[43m \u001b[49m\u001b[43mdrop_variables\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mdrop_variables\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 43\u001b[0m \u001b[43m \u001b[49m\u001b[43muse_cftime\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43muse_cftime\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 44\u001b[0m \u001b[43m \u001b[49m\u001b[43mdecode_timedelta\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mdecode_timedelta\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 45\u001b[0m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 47\u001b[0m ds \u001b[38;5;241m=\u001b[39m Dataset(\u001b[38;5;28mvars\u001b[39m, attrs\u001b[38;5;241m=\u001b[39mattrs)\n\u001b[1;32m 48\u001b[0m ds \u001b[38;5;241m=\u001b[39m ds\u001b[38;5;241m.\u001b[39mset_coords(coord_names\u001b[38;5;241m.\u001b[39mintersection(\u001b[38;5;28mvars\u001b[39m))\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/conventions.py:562\u001b[0m, in \u001b[0;36mdecode_cf_variables\u001b[0;34m(variables, attributes, concat_characters, mask_and_scale, decode_times, decode_coords, drop_variables, use_cftime, decode_timedelta)\u001b[0m\n\u001b[1;32m 551\u001b[0m new_vars[k] \u001b[38;5;241m=\u001b[39m decode_cf_variable(\n\u001b[1;32m 552\u001b[0m k,\n\u001b[1;32m 553\u001b[0m v,\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 559\u001b[0m decode_timedelta\u001b[38;5;241m=\u001b[39mdecode_timedelta,\n\u001b[1;32m 560\u001b[0m )\n\u001b[1;32m 561\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mException\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m e:\n\u001b[0;32m--> 562\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;28mtype\u001b[39m(e)(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mFailed to decode variable \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mk\u001b[38;5;132;01m!r}\u001b[39;00m\u001b[38;5;124m: \u001b[39m\u001b[38;5;132;01m{\u001b[39;00me\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m\"\u001b[39m)\n\u001b[1;32m 563\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m decode_coords \u001b[38;5;129;01min\u001b[39;00m [\u001b[38;5;28;01mTrue\u001b[39;00m, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mcoordinates\u001b[39m\u001b[38;5;124m\"\u001b[39m, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mall\u001b[39m\u001b[38;5;124m\"\u001b[39m]:\n\u001b[1;32m 564\u001b[0m var_attrs \u001b[38;5;241m=\u001b[39m new_vars[k]\u001b[38;5;241m.\u001b[39mattrs\n", - "\u001b[0;31mValueError\u001b[0m: Failed to decode variable 'time_offset': unable to decode time units 'seconds since base_time' with 'the default calendar'. Try opening your dataset with decode_times=False or installing cftime if it is not installed." - ] - } - ], - "source": [ - "# Load files as a single dataset\n", - "files_list = files_filter \n", - "ds = act.io.armfiles.read_netcdf(files_list)\n", - "ds.clean.cleanup()\n", - "print(f'{len(files_list)} files loaded')\n", - "ds\n" - ] - }, - { - "cell_type": "markdown", - "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", - "metadata": {}, - "source": [ - "## Plot time series data\n", - "#### Define the list of variables to be plotted:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d09b789e-84f1-4605-846b-a72c110c8048", - "metadata": {}, - "outputs": [], - "source": [ - "variables_to_plot = ['mwr_scale_factor', 'liquid_water_content', 'aqc_liquid_water_content']" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", - "metadata": {}, - "outputs": [], - "source": [ - "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", - "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", - "\n", - "for i,v in enumerate(variables_to_plot):\n", - " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", - " ts_ax.grid()\n", - "\n", - "plt.show()\n" - ] - }, - { - "cell_type": "markdown", - "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", - "metadata": {}, - "source": [ - "## Field selection dropdown menu\n", - "Select variable to be plotted from a dropdown menu" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", - "metadata": {}, - "outputs": [], - "source": [ - "plt.ioff()\n", - "\n", - "# populate dropdown menu with available variables \n", - "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", - "d_variable = 'mwr_scale_factor'\n", - "dropdown = widgets.Dropdown(\n", - " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", - " value= d_variable,\n", - " description='Field:',\n", - " disabled=False,\n", - ")\n", - "dropdown.layout.margin = '0px 30% 0px 20%'\n", - "dropdown.layout.width = '50%'\n", - "\n", - "# set up display\n", - "i_display = act.plotting.TimeSeriesDisplay(ds)\n", - "i_display.add_subplots((1,), figsize = (9.5,5))\n", - "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", - "i_ax.grid()\n", - "i_fig = i_display.fig\n", - "\n", - "# update plot callback function\n", - "def update_plot(change):\n", - " i_ax.cla()\n", - " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", - " i_ax_new.grid()\n", - " i_fig.canvas.draw()\n", - " i_fig.canvas.flush_events()\n", - "\n", - "dropdown.observe(update_plot, names='value')\n", - "\n", - "widgets.AppLayout(\n", - " header=dropdown,\n", - " center=i_fig.canvas,\n", - " pane_heights=[1, 6,1]\n", - ")\n" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.16" - }, - "widgets": { - "application/vnd.jupyter.widget-state+json": { - "state": {}, - "version_major": 2, - "version_minor": 0 - } - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/VAPs/quicklook/MICROBASE/.ipynb_checkpoints/microbasepiavg.c1-checkpoint.ipynb b/VAPs/quicklook/MICROBASE/.ipynb_checkpoints/microbasepiavg.c1-checkpoint.ipynb deleted file mode 100644 index 6f4b3ae0..00000000 --- a/VAPs/quicklook/MICROBASE/.ipynb_checkpoints/microbasepiavg.c1-checkpoint.ipynb +++ /dev/null @@ -1,1757 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "70840257-70e4-45e2-b491-14bff5a257a3", - "metadata": {}, - "source": [ - "# MICROBASEPIAVG.C1 Plots\n", - "\n", - "[Click here](https://www.arm.gov/capabilities/vaps/microbase) for more information about this vap." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "460fd89f-e034-452c-b837-f65c5958264f", - "metadata": {}, - "outputs": [], - "source": [ - "%matplotlib widget\n", - "import ipywidgets as widgets\n", - "\n", - "import matplotlib.pyplot as plt\n", - "import ipywidgets as widgets\n", - "import numpy as np\n", - "import pandas as pd\n", - "import os\n", - "from datetime import datetime\n", - "\n", - "import act\n", - "import xarray as xr\n", - "\n", - "# Data archive directory\n", - "DATA_DIR = r'/data/archive/'\n", - "\n", - "# Datastream info\n", - "DATASTREAM_NAME = 'microbasepiavg'\n", - "DATA_LEVEL = 'c1'\n", - "LOCATIONS = [{'end_date': '2011-03-22', 'facility': 'C1', 'site': 'nsa', 'start_date': '2002-01-01'}, {'end_date': '2010-12-30', 'facility': 'C1', 'site': 'sgp', 'start_date': '1996-11-08'}, {'end_date': '2011-02-25', 'facility': 'C1', 'site': 'twp', 'start_date': '1999-07-01'}, {'end_date': '2009-02-13', 'facility': 'C2', 'site': 'twp', 'start_date': '2002-01-01'}, {'end_date': '2011-02-27', 'facility': 'C3', 'site': 'twp', 'start_date': '2005-11-04'}]" - ] - }, - { - "cell_type": "markdown", - "id": "9faaf875", - "metadata": {}, - "source": [ - "## Define site, facility, and date range" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "ac6764f5", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "The following locations and date ranges are available for this VAP:\n" - ] - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
sitefacilitystart_dateend_date
0nsaC12002-01-012011-03-22
1sgpC11996-11-082010-12-30
2twpC11999-07-012011-02-25
3twpC22002-01-012009-02-13
4twpC32005-11-042011-02-27
\n", - "
" - ], - "text/plain": [ - " site facility start_date end_date\n", - "0 nsa C1 2002-01-01 2011-03-22\n", - "1 sgp C1 1996-11-08 2010-12-30\n", - "2 twp C1 1999-07-01 2011-02-25\n", - "3 twp C2 2002-01-01 2009-02-13\n", - "4 twp C3 2005-11-04 2011-02-27" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "print(\"The following locations and date ranges are available for this VAP:\")\n", - "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" - ] - }, - { - "cell_type": "markdown", - "id": "8d132223", - "metadata": {}, - "source": [ - "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "e563983a", - "metadata": {}, - "outputs": [], - "source": [ - "site_facility = ( 'sgp', 'C1' )\n", - "\n", - "date_start = '2010-12-28'\n", - "date_end = '2010-12-30'" - ] - }, - { - "cell_type": "markdown", - "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", - "metadata": {}, - "source": [ - "## Load data files\n", - "Load data files from /data/archive/" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'/data/archive/sgp/sgpmicrobasepiavgC1.c1'" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Compile list of files\n", - "site, facility = site_facility\n", - "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", - "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", - "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", - "dir_path\n" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "6be8f3dc", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "['20101228', '20101229', '20101230']" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "from datetime import date, timedelta\n", - "import pandas as pd\n", - "\n", - "def get_ARM_formated_dates(start_date, end_date):\n", - " \"\"\"\n", - " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", - " EXAMPLE:\n", - " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", - " >> [\"20180219\", \"20180220\", \"20180221\"] \n", - " \"\"\"\n", - " \n", - " _start_date = pd.to_datetime(start_date)\n", - " _end_date = pd.to_datetime(end_date)\n", - " \n", - " delta = _end_date - _start_date # returns timedelta \n", - " dates = []\n", - "\n", - " for i in range(delta.days + 1):\n", - " day = _start_date + timedelta(days=i)\n", - " day_formated = day.strftime(format=\"%Y%m%d\")\n", - " dates.append(day_formated)\n", - " return dates\n", - "\n", - "\n", - "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "51feea2e", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "['/data/archive/sgp/sgpmicrobasepiavgC1.c1/sgpmicrobasepiavgC1.c1.20101228.001000.cdf',\n", - " '/data/archive/sgp/sgpmicrobasepiavgC1.c1/sgpmicrobasepiavgC1.c1.20101229.001000.cdf',\n", - " '/data/archive/sgp/sgpmicrobasepiavgC1.c1/sgpmicrobasepiavgC1.c1.20101230.001000.cdf']" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Filter a list of files based on date pattern\n", - "import glob\n", - "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", - "files_filter = []\n", - "for date in dates:\n", - " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", - " files_filter\n", - "files_filter" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "id": "b0e5d0d7", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "3 files loaded\n" - ] - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "
<xarray.Dataset>\n",
-       "Dimensions:                    (time: 216, nheights: 233)\n",
-       "Coordinates:\n",
-       "  * time                       (time) datetime64[ns] 2010-12-28T00:10:00 ... ...\n",
-       "Dimensions without coordinates: nheights\n",
-       "Data variables: (12/17)\n",
-       "    base_time                  (time) datetime64[ns] 2010-12-28T00:10:00 ... ...\n",
-       "    time_offset                (time) datetime64[ns] 2010-12-28T00:10:00 ... ...\n",
-       "    Heights                    (time, nheights) float32 dask.array<chunksize=(72, 233), meta=np.ndarray>\n",
-       "    Avg_Retrieved_LWC          (time, nheights) float32 dask.array<chunksize=(72, 233), meta=np.ndarray>\n",
-       "    Avg_Retrieved_IWC          (time, nheights) float32 dask.array<chunksize=(72, 233), meta=np.ndarray>\n",
-       "    Avg_LiqEffectiveRadius     (time, nheights) float32 dask.array<chunksize=(72, 233), meta=np.ndarray>\n",
-       "    ...                         ...\n",
-       "    Integrated_CloudFraction   (time) float32 dask.array<chunksize=(72,), meta=np.ndarray>\n",
-       "    aqc_CloudFraction          (time) float32 dask.array<chunksize=(72,), meta=np.ndarray>\n",
-       "    aqc_CloudMissing           (time) float32 dask.array<chunksize=(72,), meta=np.ndarray>\n",
-       "    lat                        (time) float32 36.61 36.61 36.61 ... 36.61 36.61\n",
-       "    lon                        (time) float32 -97.49 -97.49 ... -97.49 -97.49\n",
-       "    alt                        (time) float32 318.0 318.0 318.0 ... 318.0 318.0\n",
-       "Attributes: (12/13)\n",
-       "    process_version:                $State: vap-microbasepi-1.2-1.sol5_10 $\n",
-       "    command_line:                   microbasepi -d 20101228 -f sgpC1\n",
-       "    site_id:                        sgp\n",
-       "    facility_id:                    C1: Lamont, Oklahoma\n",
-       "    input_datastreams_description:  A string consisting of the datastream(s),...\n",
-       "    input_datastreams_num:          3\n",
-       "    ...                             ...\n",
-       "    history:                        created by user dsmgr on machine garnet a...\n",
-       "    _file_dates:                    ['20101228', '20101229', '20101230']\n",
-       "    _file_times:                    ['001000', '001000', '001000']\n",
-       "    datastream:                     sgpmicrobasepiavgC1.c1\n",
-       "    _datastream:                    sgpmicrobasepiavgC1.c1\n",
-       "    _arm_standards_flag:            1
" - ], - "text/plain": [ - "\n", - "Dimensions: (time: 216, nheights: 233)\n", - "Coordinates:\n", - " * time (time) datetime64[ns] 2010-12-28T00:10:00 ... ...\n", - "Dimensions without coordinates: nheights\n", - "Data variables: (12/17)\n", - " base_time (time) datetime64[ns] 2010-12-28T00:10:00 ... ...\n", - " time_offset (time) datetime64[ns] 2010-12-28T00:10:00 ... ...\n", - " Heights (time, nheights) float32 dask.array\n", - " Avg_Retrieved_LWC (time, nheights) float32 dask.array\n", - " Avg_Retrieved_IWC (time, nheights) float32 dask.array\n", - " Avg_LiqEffectiveRadius (time, nheights) float32 dask.array\n", - " ... ...\n", - " Integrated_CloudFraction (time) float32 dask.array\n", - " aqc_CloudFraction (time) float32 dask.array\n", - " aqc_CloudMissing (time) float32 dask.array\n", - " lat (time) float32 36.61 36.61 36.61 ... 36.61 36.61\n", - " lon (time) float32 -97.49 -97.49 ... -97.49 -97.49\n", - " alt (time) float32 318.0 318.0 318.0 ... 318.0 318.0\n", - "Attributes: (12/13)\n", - " process_version: $State: vap-microbasepi-1.2-1.sol5_10 $\n", - " command_line: microbasepi -d 20101228 -f sgpC1\n", - " site_id: sgp\n", - " facility_id: C1: Lamont, Oklahoma\n", - " input_datastreams_description: A string consisting of the datastream(s),...\n", - " input_datastreams_num: 3\n", - " ... ...\n", - " history: created by user dsmgr on machine garnet a...\n", - " _file_dates: ['20101228', '20101229', '20101230']\n", - " _file_times: ['001000', '001000', '001000']\n", - " datastream: sgpmicrobasepiavgC1.c1\n", - " _datastream: sgpmicrobasepiavgC1.c1\n", - " _arm_standards_flag: 1" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Load files as a single dataset\n", - "files_list = files_filter \n", - "ds = act.io.armfiles.read_netcdf(files_list)\n", - "ds.clean.cleanup()\n", - "print(f'{len(files_list)} files loaded')\n", - "ds\n" - ] - }, - { - "cell_type": "markdown", - "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", - "metadata": {}, - "source": [ - "## Plot time series data\n", - "#### Define the list of variables to be plotted:" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "id": "d09b789e-84f1-4605-846b-a72c110c8048", - "metadata": {}, - "outputs": [], - "source": [ - "variables_to_plot = ['avg_retrieved_lwc', 'avg_retrieved_iwc', 'avg_liq_effective_radius']\n", - "variables_to_plot = ['avg_retrieved_iwc', 'avg_liq_effective_radius']" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", - "metadata": {}, - "outputs": [ - { - "ename": "KeyError", - "evalue": "'avg_retrieved_iwc'", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mKeyError\u001b[0m Traceback (most recent call last)", - "Cell \u001b[0;32mIn[11], line 5\u001b[0m\n\u001b[1;32m 2\u001b[0m ts_display\u001b[38;5;241m.\u001b[39madd_subplots((\u001b[38;5;28mlen\u001b[39m(variables_to_plot),), figsize \u001b[38;5;241m=\u001b[39m (\u001b[38;5;241m9.5\u001b[39m,\u001b[38;5;241m4\u001b[39m\u001b[38;5;241m*\u001b[39m\u001b[38;5;28mlen\u001b[39m(variables_to_plot)))\n\u001b[1;32m 4\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m i,v \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28menumerate\u001b[39m(variables_to_plot):\n\u001b[0;32m----> 5\u001b[0m ts_ax \u001b[38;5;241m=\u001b[39m ts_display\u001b[38;5;241m.\u001b[39mplot(v, subplot_index\u001b[38;5;241m=\u001b[39m(i,), set_title\u001b[38;5;241m=\u001b[39m\u001b[43mds\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mvariables\u001b[49m\u001b[43m[\u001b[49m\u001b[43mv\u001b[49m\u001b[43m]\u001b[49m\u001b[38;5;241m.\u001b[39mattrs[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mlong_name\u001b[39m\u001b[38;5;124m'\u001b[39m],)\n\u001b[1;32m 6\u001b[0m ts_ax\u001b[38;5;241m.\u001b[39mgrid()\n\u001b[1;32m 8\u001b[0m plt\u001b[38;5;241m.\u001b[39mshow()\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/core/utils.py:453\u001b[0m, in \u001b[0;36mFrozen.__getitem__\u001b[0;34m(self, key)\u001b[0m\n\u001b[1;32m 452\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21m__getitem__\u001b[39m(\u001b[38;5;28mself\u001b[39m, key: K) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m V:\n\u001b[0;32m--> 453\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mmapping\u001b[49m\u001b[43m[\u001b[49m\u001b[43mkey\u001b[49m\u001b[43m]\u001b[49m\n", - "\u001b[0;31mKeyError\u001b[0m: 'avg_retrieved_iwc'" - ] - }, - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "fa59298dbac54ea38a2f036fff77b0e0", - "version_major": 2, - "version_minor": 0 - }, - "image/png": "iVBORw0KGgoAAAANSUhEUgAAA7YAAAMgCAYAAAAXxf7GAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAA9hAAAPYQGoP6dpAAA9KElEQVR4nO3df2zV9b348Vdpaatu7SLMWgS7uquTXTI22sAoaxad1oDhhmQLXVysejFZs+0S6HQDWXQQk2ZbZnadglsEzRJ0jT/jH52jWTZ+CDcZTVkWIXeLcC1sraQ1a1G3IvD5/mHod12LUqSn5w2PR3L+OO99PvR1trfdefL5HE9BlmVZAAAAQKKmTPYAAAAA8GEIWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrCdYDt27IilS5fGjBkzoqCgIF588cUPPGf79u1RU1MTpaWlcc0118Rjjz028YMCAAAkSthOsLfffjvmzp0b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UvLdePbS888/HzfffHN8/OMfj7Kysli4cGH8+te/zuG05LPx/l467ZVXXomioqL47Gc/O7EDkozx7qWhoaFYt25dVFVVRUlJSXzyk5+MLVu25Gha8t1499PWrVtj7ty5cemll0ZlZWXcdddd0d/fn6NpyVc7duyIpUuXxowZM6KgoCBefPHFDzzH++/cEbaJamtri1WrVsW6deuiq6sr6uvrY/HixSO+NuifHTp0KJYsWRL19fXR1dUV9913X6xcuTKee+65HE9OvhnvXtqxY0fcfPPN0d7eHp2dnXHDDTfE0qVLo6urK8eTk2/Gu5dOGxgYiKampvjSl76Uo0nJd+eyl5YvXx6/+c1vYvPmzfG///u/8fTTT8f111+fw6nJV+PdT7t27YqmpqZYsWJFvPrqq/HMM8/E73//+7j77rtzPDn55u233465c+fGI488clbHe/+dYxlJmj9/ftbc3Dxi7frrr8/WrFkz5vHf+c53suuvv37E2te//vXs85///ITNSBrGu5fG8ulPfzpbv379+R6NxJzrXmpsbMy+973vZQ888EA2d+7cCZyQVIx3L/3qV7/KysvLs/7+/lyMR2LGu59+9KMfZddcc82ItYcffjibOXPmhM1IeiIie+GFF973GO+/c8sV2wQdP348Ojs7o6GhYcR6Q0ND7N69e8xz9uzZM+r4W265Jfbu3RvvvvvuhM1KfjuXvfSvTp06FceOHYvLL798IkYkEee6l5544ol47bXX4oEHHpjoEUnEueyll156KWpra+OHP/xhXHXVVXHdddfFPffcE3//+99zMTJ57Fz2U11dXRw5ciTa29sjy7J444034tlnn41bb701FyNzAfH+O7eKJnsAxq+vry9OnjwZFRUVI9YrKiqit7d3zHN6e3vHPP7EiRPR19cXlZWVEzYv+etc9tK/+vGPfxxvv/12LF++fCJGJBHnspf+/Oc/x5o1a2Lnzp1RVOT/jnjPueylgwcPxq5du6K0tDReeOGF6Ovri2984xvx5ptv+pztRe5c9lNdXV1s3bo1Ghsb4x//+EecOHEi/uM//iN++tOf5mJkLiDef+eWK7YJKygoGPE8y7JRax90/FjrXHzGu5dOe/rpp+P73/9+tLW1xRVXXDFR45GQs91LJ0+ejNtuuy3Wr18f1113Xa7GIyHj+b106tSpKCgoiK1bt8b8+fNjyZIl8dBDD8WTTz7pqi0RMb79tH///li5cmXcf//90dnZGS+//HIcOnQompubczEqFxjvv3PHX5EnaPr06VFYWDjqbxqPHj066m+FTrvyyivHPL6oqCimTZs2YbOS385lL53W1tYWK1asiGeeeSZuuummiRyTBIx3Lx07diz27t0bXV1d8a1vfSsi3ouTLMuiqKgotm3bFjfeeGNOZie/nMvvpcrKyrjqqquivLx8eG327NmRZVkcOXIkrr322gmdmfx1LvuptbU1Fi1aFPfee29ERHzmM5+Jyy67LOrr6+PBBx90lY2z5v13brlim6Di4uKoqamJjo6OEesdHR1RV1c35jkLFy4cdfy2bduitrY2pk6dOmGzkt/OZS9FvHel9s4774ynnnrKZ46IiPHvpbKysvjjH/8Y+/btG340NzfHpz71qdi3b18sWLAgV6OTZ87l99KiRYvir3/9a7z11lvDa3/6059iypQpMXPmzAmdl/x2LvvpnXfeiSlTRr5FLiwsjIj/f7UNzob33zk2Sf/SKj6kX/7yl9nUqVOzzZs3Z/v3789WrVqVXXbZZdn//d//ZVmWZWvWrMluv/324eMPHjyYXXrppdnq1auz/fv3Z5s3b86mTp2aPfvss5P1EsgT491LTz31VFZUVJQ9+uijWU9Pz/Djb3/722S9BPLEePfSv/JvRea08e6lY8eOZTNnzsy+8pWvZK+++mq2ffv27Nprr83uvvvuyXoJ5JHx7qcnnngiKyoqyjZu3Ji99tpr2a5du7La2tps/vz5k/USyBPHjh3Lurq6sq6uriwisoceeijr6urKXn/99SzLvP+ebMI2YY8++mhWVVWVFRcXZ/Pmzcu2b98+/J/dcccd2Re/+MURx//ud7/LPve5z2XFxcXZJz7xiWzTpk05nph8NZ699MUvfjGLiFGPO+64I/eDk3fG+3vpnwlb/tl499KBAweym266KbvkkkuymTNnZi0tLdk777yT46nJV+PdTw8//HD26U9/OrvkkkuyysrK7Gtf+1p25MiRHE9Nvvntb3/7vu+BvP+eXAVZ5p4KAAAA0uUztgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsA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zFpaWrJ33nknx1OTr8a7nx5++OHs05/+dHbJJZdklZWV2de+9rXsyJEjOZ6afPPb3/72fd8Def89uQqyzD0VAAAApMtnbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgaf8PfYg0zZCMkMIAAAAASUVORK5CYII=", - "text/html": [ - "\n", - "
\n", - "
\n", - " Figure\n", - "
\n", - " \n", - "
\n", - " " - ], - "text/plain": [ - "Canvas(toolbar=Toolbar(toolitems=[('Home', 'Reset original view', 'home', 'home'), ('Back', 'Back to previous …" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", - "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", - "\n", - "for i,v in enumerate(variables_to_plot):\n", - " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", - " ts_ax.grid()\n", - "\n", - "plt.show()\n" - ] - }, - { - "cell_type": "markdown", - "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", - "metadata": {}, - "source": [ - "## Field selection dropdown menu\n", - "Select variable to be plotted from a dropdown menu" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", - "metadata": {}, - "outputs": [], - "source": [ - "plt.ioff()\n", - "\n", - "# populate dropdown menu with available variables \n", - "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", - "d_variable = 'avg_retrieved_lwc'\n", - "dropdown = widgets.Dropdown(\n", - " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", - " value= d_variable,\n", - " description='Field:',\n", - " disabled=False,\n", - ")\n", - "dropdown.layout.margin = '0px 30% 0px 20%'\n", - "dropdown.layout.width = '50%'\n", - "\n", - "# set up display\n", - "i_display = act.plotting.TimeSeriesDisplay(ds)\n", - "i_display.add_subplots((1,), figsize = (9.5,5))\n", - "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", - "i_ax.grid()\n", - "i_fig = i_display.fig\n", - "\n", - "# update plot callback function\n", - "def update_plot(change):\n", - " i_ax.cla()\n", - " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", - " i_ax_new.grid()\n", - " i_fig.canvas.draw()\n", - " i_fig.canvas.flush_events()\n", - "\n", - "dropdown.observe(update_plot, names='value')\n", - "\n", - "widgets.AppLayout(\n", - " header=dropdown,\n", - " center=i_fig.canvas,\n", - " pane_heights=[1, 6,1]\n", - ")\n" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.16" - }, - "widgets": { - "application/vnd.jupyter.widget-state+json": { - "state": {}, - "version_major": 2, - "version_minor": 0 - } - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/VAPs/quicklook/MPLNOR/.ipynb_checkpoints/mplnor1camp.c1-checkpoint.ipynb b/VAPs/quicklook/MPLNOR/.ipynb_checkpoints/mplnor1camp.c1-checkpoint.ipynb deleted file mode 100644 index d37d9027..00000000 --- a/VAPs/quicklook/MPLNOR/.ipynb_checkpoints/mplnor1camp.c1-checkpoint.ipynb +++ /dev/null @@ -1,1732 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "70840257-70e4-45e2-b491-14bff5a257a3", - "metadata": {}, - "source": [ - "# MPLNOR1CAMP.C1 Plots\n", - "\n", - "[Click here](https://www.arm.gov/capabilities/vaps/mplnor) for more information about this vap." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "460fd89f-e034-452c-b837-f65c5958264f", - "metadata": {}, - "outputs": [], - "source": [ - "%matplotlib widget\n", - "import ipywidgets as widgets\n", - "\n", - "import matplotlib.pyplot as plt\n", - "import ipywidgets as widgets\n", - "import numpy as np\n", - "import pandas as pd\n", - "import os\n", - "from datetime import datetime\n", - "\n", - "import act\n", - "import xarray as xr\n", - "\n", - "# Data archive directory\n", - "DATA_DIR = r'/data/archive/'\n", - "\n", - "# Datastream info\n", - "DATASTREAM_NAME = 'mplnor1camp'\n", - "DATA_LEVEL = 'c1'\n", - "LOCATIONS = [{'end_date': '2004-05-11', 'facility': 'C1', 'site': 'sgp', 'start_date': '1996-05-01'}, {'end_date': '1999-11-18', 'facility': 'C2', 'site': 'twp', 'start_date': '1998-11-20'}]" - ] - }, - { - "cell_type": "markdown", - "id": "9faaf875", - "metadata": {}, - "source": [ - "## Define site, facility, and date range" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "ac6764f5", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "The following locations and date ranges are available for this VAP:\n" - ] - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
sitefacilitystart_dateend_date
0sgpC11996-05-012004-05-11
1twpC21998-11-201999-11-18
\n", - "
" - ], - "text/plain": [ - " site facility start_date end_date\n", - "0 sgp C1 1996-05-01 2004-05-11\n", - "1 twp C2 1998-11-20 1999-11-18" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "print(\"The following locations and date ranges are available for this VAP:\")\n", - "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" - ] - }, - { - "cell_type": "markdown", - "id": "8d132223", - "metadata": {}, - "source": [ - "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "e563983a", - "metadata": {}, - "outputs": [], - "source": [ - "site_facility = ( 'sgp', 'C1' )\n", - "\n", - "date_start = '2004-05-10'\n", - "date_end = '2004-05-11'" - ] - }, - { - "cell_type": "markdown", - "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", - "metadata": {}, - "source": [ - "## Load data files\n", - "Load data files from /data/archive/" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'/data/archive/sgp/sgpmplnor1campC1.c1'" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Compile list of files\n", - "site, facility = site_facility\n", - "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", - "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", - "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", - "dir_path\n" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "6be8f3dc", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "['20040510', '20040511']" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "from datetime import date, timedelta\n", - "import pandas as pd\n", - "\n", - "def get_ARM_formated_dates(start_date, end_date):\n", - " \"\"\"\n", - " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", - " EXAMPLE:\n", - " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", - " >> [\"20180219\", \"20180220\", \"20180221\"] \n", - " \"\"\"\n", - " \n", - " _start_date = pd.to_datetime(start_date)\n", - " _end_date = pd.to_datetime(end_date)\n", - " \n", - " delta = _end_date - _start_date # returns timedelta \n", - " dates = []\n", - "\n", - " for i in range(delta.days + 1):\n", - " day = _start_date + timedelta(days=i)\n", - " day_formated = day.strftime(format=\"%Y%m%d\")\n", - " dates.append(day_formated)\n", - " return dates\n", - "\n", - "\n", - "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "51feea2e", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "['/data/archive/sgp/sgpmplnor1campC1.c1/sgpmplnor1campC1.c1.20040510.000020.cdf',\n", - " '/data/archive/sgp/sgpmplnor1campC1.c1/sgpmplnor1campC1.c1.20040511.000011.cdf']" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Filter a list of files based on date pattern\n", - "import glob\n", - "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", - "files_filter = []\n", - "for date in dates:\n", - " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", - " files_filter\n", - "files_filter" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "id": "b0e5d0d7", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "77 files loaded\n" - ] - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "
<xarray.Dataset>\n",
-       "Dimensions:                     (time: 1420, height: 445, nlayers: 5)\n",
-       "Coordinates:\n",
-       "  * height                      (height) float32 0.02998 0.1199 ... 39.88 39.97\n",
-       "  * time                        (time) datetime64[ns] 2004-05-10T00:00:20 ......\n",
-       "Dimensions without coordinates: nlayers\n",
-       "Data variables: (12/20)\n",
-       "    base_time                   datetime64[ns] 2004-05-10T00:00:20\n",
-       "    time_offset                 (time) datetime64[ns] 2004-05-10T00:00:20 ......\n",
-       "    backscatter                 (time, height) float32 dask.array<chunksize=(1420, 445), meta=np.ndarray>\n",
-       "    background_signal           (time) float32 dask.array<chunksize=(1420,), meta=np.ndarray>\n",
-       "    cloud_base_height           (time, nlayers) float32 dask.array<chunksize=(1420, 5), meta=np.ndarray>\n",
-       "    cloud_top_height            (time, nlayers) float32 dask.array<chunksize=(1420, 5), meta=np.ndarray>\n",
-       "    ...                          ...\n",
-       "    detector_temp               (time) float32 dask.array<chunksize=(1420,), meta=np.ndarray>\n",
-       "    instrument_temp             (time) float32 dask.array<chunksize=(1420,), meta=np.ndarray>\n",
-       "    laser_temp                  (time) float32 dask.array<chunksize=(1420,), meta=np.ndarray>\n",
-       "    lat                         float32 ...\n",
-       "    lon                         float32 ...\n",
-       "    alt                         float32 ...\n",
-       "Attributes: (12/24)\n",
-       "    Date:                        Tue May 11 19:11:20 2004\n",
-       "    Version:                     $State: process-vap-mplnor-2.9-0 $\n",
-       "    Command_Line:                mplnor -d 20040510\n",
-       "    Input_Platforms:             sgpmplC1.a1\n",
-       "    BW_Version:                  Working_4_1\n",
-       "    Comment:                     Pass-through VAP to improve the data quality\n",
-       "    ...                          ...\n",
-       "    history:                     created by user dsmgr on machine fore at 11-...\n",
-       "    _file_dates:                 ['20040510']\n",
-       "    _file_times:                 ['000020']\n",
-       "    datastream:                  sgpmplnor1campC1.c1\n",
-       "    _datastream:                 sgpmplnor1campC1.c1\n",
-       "    _arm_standards_flag:         1
" - ], - "text/plain": [ - "\n", - "Dimensions: (time: 1420, height: 445, nlayers: 5)\n", - "Coordinates:\n", - " * height (height) float32 0.02998 0.1199 ... 39.88 39.97\n", - " * time (time) datetime64[ns] 2004-05-10T00:00:20 ......\n", - "Dimensions without coordinates: nlayers\n", - "Data variables: (12/20)\n", - " base_time datetime64[ns] 2004-05-10T00:00:20\n", - " time_offset (time) datetime64[ns] 2004-05-10T00:00:20 ......\n", - " backscatter (time, height) float32 dask.array\n", - " background_signal (time) float32 dask.array\n", - " cloud_base_height (time, nlayers) float32 dask.array\n", - " cloud_top_height (time, nlayers) float32 dask.array\n", - " ... ...\n", - " detector_temp (time) float32 dask.array\n", - " instrument_temp (time) float32 dask.array\n", - " laser_temp (time) float32 dask.array\n", - " lat float32 ...\n", - " lon float32 ...\n", - " alt float32 ...\n", - "Attributes: (12/24)\n", - " Date: Tue May 11 19:11:20 2004\n", - " Version: $State: process-vap-mplnor-2.9-0 $\n", - " Command_Line: mplnor -d 20040510\n", - " Input_Platforms: sgpmplC1.a1\n", - " BW_Version: Working_4_1\n", - " Comment: Pass-through VAP to improve the data quality\n", - " ... ...\n", - " history: created by user dsmgr on machine fore at 11-...\n", - " _file_dates: ['20040510']\n", - " _file_times: ['000020']\n", - " datastream: sgpmplnor1campC1.c1\n", - " _datastream: sgpmplnor1campC1.c1\n", - " _arm_standards_flag: 1" - ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Load files as a single dataset\n", - "files_list = files_filter[0]\n", - "ds = act.io.armfiles.read_netcdf(files_list)\n", - "ds.clean.cleanup()\n", - "print(f'{len(files_list)} files loaded')\n", - "ds\n" - ] - }, - { - "cell_type": "markdown", - "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", - "metadata": {}, - "source": [ - "## Plot time series data\n", - "#### Define the list of variables to be plotted:" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "id": "d09b789e-84f1-4605-846b-a72c110c8048", - "metadata": {}, - "outputs": [], - "source": [ - "variables_to_plot = ['backscatter', 'cloud_base_height', 'cloud_top_height']" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", - "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "b62b704e7cb241cea9f9f03bf8d662ea", - "version_major": 2, - "version_minor": 0 - }, - "image/png": "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APzjH//weH7JJZegYcOGeP/9903dxiY7O9sdlHfs2BG7du3SPE6SJLRo0QKvvvoqqlat6n6/y8N9Z86cqVuH2bYrj9F6jRwOh+Z2s/8fEBFFGmZsieiCM23aNFx55ZU4ePCge3gdAHTr1g3XXXcdnnzySRQVFaF9+/buVZFbtmyJO+64I+Rt69atG+x2OwYNGoQxY8aguLgYM2fOxNGjRwMqr2fPnjh27BjefPNN7Nixw2Nfw4YNUatWLTz33HNYsWIF2rVrh4cffhiNGzdGcXExdu/ejSVLluDtt99G3bp1ceedd+LVV1/FnXfeieeffx6NGjXCkiVLsGzZMlNtee655/Dll1/immuuwbhx43D55Zfj2LFjWLp0KUaPHo1LL70Ujz76KObOnYvevXvjueeeQ3Z2NhYvXowZM2bggQce0J3TbEb79u1x3333YdiwYdi0aROuueYaJCUlYf/+/fj2229x+eWX44EHHkCVKlXw8ssv45577kHXrl1x7733Ii0tDb/++iu+//57vPnmmwDgDrKmTZuGnj17wmazoXnz5qbrqVatGh5//HFMnjwZ99xzD26++WYUFBQgNzfXr6HIWl5//XV06NABHTt2xAMPPICLLroIJ06cwK+//oovvvjCa66y0vXXX4/Zs2fj0ksvRfPmzbF582a89NJLXsNxGzZsiISEBHz44Ye47LLLUKVKFWRmZhoGPoMGDcLo0aMxaNAglJSUeM1nN9t3vixYsACxsbHo1q2be1XkFi1a4JZbbgFw9suLatWq4f7778eECRMQFxeHDz/8UPNe12+99Rb69OmDNm3a4NFHH0W9evWwd+9eLFu2zCuABs4OzV2zZg2uu+46XHPNNVixYgWaNWuGRYsWYcaMGbjxxhvRoEEDCCGwYMECHDt2DN26dQMAdOzYEXfccQcmT56MAwcO4Prrr4fD4cDWrVuRmJiIhx56yK+2671H7XY7Lr/8cqxevRpffPEFMjIykJycjMaNG5v+/4CIKOKEZ80qIqLQU66KrDZ48GABwGNVZCGEOHPmjHjyySdFdna2iIuLExkZGeKBBx4QR48e9TguOztb9O7d26tceVXk//u//zPVFuVqqrIvvvhCtGjRQsTHx4s6deqIJ554Qnz55ZcCgFi1apX7ODOrIgPQfShXs/3rr7/Eww8/LOrXry/i4uJE9erVxZVXXimefvppcfLkSfdxf/zxhxgwYICoUqWKSE5OFgMGDBDr1q0ztSqyEGdXt73rrrtEenq6iIuLE5mZmeKWW24RBw4ccB+zZ88eMXjwYFGjRg0RFxcnGjduLF566SWPFWblFVlfeuklrzq0+lTp/fffF61btxZJSUkiISFBNGzYUNx5551i06ZNHsctWbJE5OTkiKSkJJGYmCiaNGkipk2b5t5fUlIi7rnnHlGrVi0hSZIAIPLz8/2qx+VyialTp4qsrCxht9tF8+bNxRdffCFycnJMr4o8cuRIzX35+fnirrvuEnXq1BFxcXGiVq1aol27dmLy5Mle/ah87Y4ePSruvvtuUbt2bZGYmCg6dOgg1q5dq9mmf//73+LSSy8VcXFxAoCYMGGCEMJ7VWQl+Xevffv2utdl9jVSk+vdvHmz6NOnj/t9OmjQII/3mBBCrFu3TrRt21YkJiaKWrVqiXvuuUds2bJF8728fv160bNnT5GamiocDodo2LChx8rLWu+5Y8eOifbt24vq1auLvLw88fPPP4tBgwaJhg0bioSEBJGamiquvvpqMXv2bI+6nE6nePXVV0WzZs2E3W4Xqampom3btuKLL77wu+1G79Ft27aJ9u3bi8TERK9VuM38f2D0O0hEFA6SEAbjrIiIiIiIiIgiHOfYEhERERERUVRjYEtERERERERRjYEtERERERERRTUGtmE2depUSJKEUaNGubcJIZCbm4vMzEwkJCSgU6dOXquZEhERERER0VkMbMMoLy8P77zzDpo3b+6x/cUXX8Qrr7yCN998E3l5eUhPT0e3bt1w4sSJMLWUiIiIiIgocjGwDZOTJ0/itttuw7vvvotq1aq5twsh8Nprr+Hpp59G//790axZM8yZMwenT5/GvHnzwthiIiIiIiKiyBQb7gZcqEaOHInevXuja9eumDx5snt7fn4+CgsL0b17d/c2h8OBnJwcrFu3DsOHD9csr6SkBCUlJe7nLpcLR44cQY0aNSBJUuguhIiIiIiIIITAiRMnkJmZiZgY4/xhcXExSktLDY+x2+2Ij48PZhMrNQa2YTB//nxs2bIFeXl5XvsKCwsBAGlpaR7b09LSsGfPHt0yp06diokTJwa3oURERERE5JeCggLUrVtXd39xcTHqZ1dB4UGnYTnp6enIz89ncGsSA1uLFRQU4JFHHsHy5csN36Tq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kRjYmvTtt9/i22+/RYsWLcLdFCIiIopUDGqJyCQzQ5FTUlJM3ce2WrVqAICqVavi0KFD2LNnD0pLS5GXl4fmzZsHq8kRjYGtSZdeeinOnDkT7mYQEREREVFlEKTFo7p3745//etfyMnJQUlJCV555RXs3bsXOTk5iI+Px9y5c02V8+CDD+LNN980X3GEYWBr0owZM/DUU09h/PjxaNasGeLi4jz2m/kmhYiIiCo5DkUmIovZbDbMmzfPY1u7du0wcOBAv8qJjY3F/fffj7fffjuYzbNMTLgbEC2qVq2K48ePo0uXLqhduzaqVauGatWqoWrVqu7UPxEREV3gGNQSkVni/HBk9cOfjG2wvPbaazh69Cjuuece6ysPAmZsTbrttttgt9sxb948Lh5FREREREQVE6ShyME0ffp0NGrUCP/85z/D04AKYGBr0o8//oitW7eicePG4W4KERERERFFuwgLbE+fPo3+/fvj5Zdftr7yIOBQZJNatWqFgoKCcDeDiIiIiIgqAb1hyEarJYfSLbfcgrvvvhsjR460vvIgYMbWpIceegiPPPIInnjiCVx++eVei0ddKMtoExERERFR5XPjjTdG7fxagIGtabfeeisA4K677nJvkyQJQghIkgSn0xmuphERERERUbSJsKHI0RzUAgxsTcvPzw93E4iIiCjSuQQQwwUmicg3oyHHoR6K/Oeff6JOnTpe20tKSgAADocjtA0IAc6xNSk1NRXZ2dmaj7KysnA3j4iIiCIBg1oi8ofQeYRYVlYWXnnlFa/tq1at8vv+t5GCga1JvXr1QnFxsdf2X375BZ06dbK+QUREREREFL30gloLgtuGDRti3rx5mD59usf2Hj164Keffgpt5SHCwNakatWq4cYbb0R5ebl7208//YROnTphwIABYWwZERERRQze556ITArnqsjJycn4+uuv8fHHH2PKlCke+xITE0NbeYgwsDXpk08+walTpzB48GAIIfDjjz+iU6dOGDRoEF5//fVwN4+IiIgigQjDii9EFJ3CmLEVQiA1NRVff/01Fi9ejCFDhmDz5s146623kJ6eHtrKQ4SBrUnx8fFYtGgRdu3ahZtvvhnXXnst7rzzTs2x6URERHSBcjGwJSJzwpmxjY+PB3A+c1urVi0MGjQIn3/+Od5+++3QVh4iXBXZQFFRkcdzSZLw0UcfoWvXrhgwYACeffZZ9zEpKSnhaCIRERFFEi4eRURmhfF2P+vXr3f/HB8fj+nTp3vNt402DGwNVK1aFZLGXBkhBN5++2384x//4H1siYiIiIjIfxF2H9tox8DWwKpVq8LdBCIiIoomksR5tkRkSjjvY1sZMbA1kJOTE+4mEBERUTRhUEtEZjFjG1RcPMrA3r17/Tr+zz//DFFLiIiIiIiIKm7Dhg3hbkJIMLA1cNVVV+Hee+/Fxo0bdY85fvw43n33XTRr1gwLFiwwVe4333yDPn36IDMzE5IkYeHChR77hw4dCkmSPB5t2rSpyKUQERGRFXgfWyIyK0y3+3n++efRsGFDjBgxAsuXL0d5eXnoKrMQhyIb+OmnnzBlyhT06NEDcXFxaNWqFTIzMxEfH4+jR49i586d2LFjB1q1aoWXXnoJPXv2NFXuqVOn0KJFCwwbNgwDBgzQPKZHjx6YNWuW+7ndbg/KNREREVEIcSgyEZkUrjm2n3/+OU6fPo2lS5fiX//6F+6//360bdsW/fr1Q8+ePZGUlBS6ykOIga2B6tWrY/r06Zg8eTKWLFmCtWvXYvfu3Thz5gxq1qyJ2267Dddddx2aNWvmV7k9e/b0GQQ7HI6ovTkyERERERH5EMY5tomJiejfvz/69+8Pp9OJVatWYeHChXjqqadw6aWXYtGiRaFtQAgwsDUhPj7e/cJbZfXq1ahduzaqVq2KnJwcPP/886hdu7Zl9RMRERERUehEyqrINpsNXbt2RdeuXQEAeXl51lUeRAxsI1DPnj1x8803Izs7G/n5+Xj22WfRpUsXbN68GQ6HQ/OckpISlJSUuJ8XFRUBAGLjYxEXw5c51OLiYz3+pdBif1uL/W0t9re12N/WY59bi/1tIZcLOOPH8RG6KvJVV10VvsorQBKCk0HCSZIkfPrpp7jxxht1j9m/fz+ys7Mxf/583axxbm4uJk6c6LV93rx5SExMDFZziYiIiIhIw+nTpzF48GAcP34cKSkpuscVFRUhNTUVl42YApsjXvMYZ0kxfpoxzmdZstWrV2PSpEkoLy/H6NGjUVxcjNdeew0JCQmYM2cOsrKyAr6uaMGvbqJARkYGsrOzsWvXLt1jxo4di9GjR7ufFxUVISsrC7NHLkZcDBeeCrW4+FgMfas3Zo9cjLLiyrGyXCRjf1uL/W0t9re12N/WY59bi/1tnTJXqV/HS+ceevvMKi4uxssvv4wvv/wSdrsdZWVl6NChA9auXYu8vDxMmjQJ77zzjl9ti0YMbKPA4cOHUVBQgIyMDN1jHA6H5jDl8uJyQOJdnaxSVlyOsjP8o2EV9re12N/WYn9bi/1tPfa5tdjfoVcu/OzfIA1FXrduHRISEtCnTx8kJibiiSeeQNOmTWG329G+fXs8/vjjuucWFRXh9OnTqFmzJmJjozs0ZMQTBidPnsS2bduwbds2AEB+fj62bduGvXv34uTJk3j88cexfv167N69G6tXr0afPn1Qs2ZN9OvXL7wNJyIiIiKioJAXj9J7AGcDT+VDuaaO7MCBA8jPz8cXX3yB++67D7m5uR7Dl51Op8fxe/fuxRNPPIEGDRogLS0Nl112GVJSUtC1a1f83//9X0ivOZQY2IbBpk2b0LJlS7Rs2RIAMHr0aLRs2RLjx4+HzWbD9u3b0bdvX1xyySUYMmQILrnkEqxfvx7JyclhbjkREREREQWF8PEAkJWVhdTUVPdj6tSpXsVUrVoVHTp0gN1uR5cuXbB161b3QrLA2VWPZQsWLMDNN9+M+vXrY+XKlThz5gyOHj2KI0eO4Omnn8ayZcvQoUOH0F1zCEV3vjlKderUCUZrdi1btszC1hARERERUVj4GHJcUFDgkX3Vmnp49dVX47XXXgMAbN26Fd27d8fOnTtRWlqKvLw8NG/e3H1smzZtsGHDBq8y4uPj0blzZ3Tu3BkFBQWBXUuYMbAlIiIiIiKymJn72KakpPhcFblGjRq44YYbcM011yAmJgbvv/8+Nm7ciJycHMTHx2Pu3LnuYzMzM322K1pXUGZgS0RERBQskgTwTopEZEYQ72M7cuRIjBw50v28QYMGGDhwoO7x+fn5mDZtGn777TeUl59f9GrVqlX+VRxBGNgSERERERFZzEzGNlRuvfVWdO/eHb179/aYgxvNGNgSERERBQuztURkVhAztn5XLQQmT54c2kosxlWRiYiIiIiILGbmdj+h0rhxY+Tn54e2EosxY0tERERERHQBOXz4MK644gp06NAB8fHx7u3RfB9bBrZERERERERWC+NQ5EGDBmHQoEGhrcRiDGyJiIiIiIisFsbAduDAgYiNjUVMTOWZmVp5roSIiIiIiChKhHOObdeuXd1zbAcMGICqVavin//8Z2grDTEGtkRERERERFYTPh4hdOzYMTRs2BBbtmzBiRMn8PPPP+PVV18NbaUhxqHIREREkUiStLfxdjJERJWCJAQknf/T9bYHi3zv2tWrV+P6669Heno6HA5HSOsMNQa2RBR6Wh/QZfyQTnSe+ndFL7gF+LtDFAn4ZRNVRBjn2NauXRtjx47Ff/7zHyxZsgROpxNOpzO0lYYYhyITUehIEqTYWEg2m+cjJsb9gCQZB75EFxLlB2QhjB8UmVx8bS4oRr+LWl9U8e8dKYRzju2sWbNQVlaG559/Ho0aNUJJSUnUz7FlxpaIQkuI83/Izw17UX4QkM592y1cLnNlERFFshgGLheUYGRslWVUpDxmj6NPGDO2mZmZmD59uvt5YmIirrrqqtBWGmIMbIkodISAUA5rUfws2e1nf4iJAVyus9lbrSKUAa8cIF9I33grh51q/axF+cFGPWxV/uCj/Fd9jlFbzPQ9P1gR0YXC6P879T69Y9UjNeiCYZSZDXXGtjJiYEtE1lH8wRYlJQAAyWYDYmN1v62W5CyvHOCqgzatQCsY33xrUX+rbsRX8BlI3Vo/+zrW6HwzZQZyHWaPVwbYZs6trB/4zMyr1TuvsvYJEdGFIIwZ28qIgS0RhZVwuc6v/KcOuGKk8/PVbLazH+JdLkhSzLnDzs7T9RrG7E8Q6G6IRgCnFXD5E3QEmea1RrNgZuAr44JKWl/caH0ZQEQE8P+FKMSMbXBx8SgiCi8hIEpLzz9KSs4/zhS7t6O8/OzxcoZX/lljQSpfDwBez9UBhMfCVuf26Q2XNlOP+mdleVpt0iorbEGt1oJGwSzTzLFax6teH69tegFzOIeyB5qdBc73gfpfIiKKTmG8j63aqFGjAAAPPfSQtRUHETO2RBQ5tD6on9smnE73HF1hOxfgyYHeuXm67n991OEOWs+VbRSwmtnmUbxivrBWECsHp1pBrVYZ/gazvtrnd5nBWsXT7LxgM/duNRo2rXdOpDAz587XMcrrj8RrJCIi0yIlM9umTRuMHTsW7du3D3dTAsbAloiijzvYLYcoc54PVM0EbHLA4yMgEBoLWmlt09qnd66vupTH6B6vtfDTOWbapj7eV5DrdV6gQa6vTKWZcv2ZW+zPgi6RSu99Gi3tJyIiY0afRyz8v/6uu+5CeXk5PvzwQwwePBjLli3DrFmzLKs/WBjYElF0k1de1gr2VM+Ngk11kKfOoLrr8hGguoNuHwGjer/X8XpZOvW/ygW5DG6srtUeM5nbs8eEd9aKP18SnD9J/33g3kZERBRGkTLH9v3338eMGTPQoUMHlJeXY8SIEdZVHkScY0tElYNy3qHyG1DFc+F0ejzU+zSP1brdkLJcZd3KY5TtCOShdX1mjjPqn4q2x5/9QSRcLvcD8J6r7JPe8OYL6bZRREQUeSJoju2xY8dw33334fDhw9ZWHETM2BIRGfEVpHEhn7PMzB0NhCr41Bq2Hczyz1d0gb+eRER0QRk3bhwA4Nlnnw1zSwLHjG0YfPPNN+jTpw8yMzMhSRIWLlzosV8IgdzcXGRmZiIhIQGdOnXCjh07wtNYIqJw0sgOq7PrQc2Oy9vUqyvrPYiIiAIkuYwf5B8GtmFw6tQptGjRAm+++abm/hdffBGvvPIK3nzzTeTl5SE9PR3dunXDiRMnLG4pEUW8SLylTbTRCnL9CYoZ5BIRUSAiaChyZcDANgx69uyJyZMno3///l77hBB47bXX8PTTT6N///5o1qwZ5syZg9OnT2PevHlhaC0RRTSthZFkDLasEYJ5xUREVPnJi0fpPfz173//G7Vq1QIAfPTRR2jbti26dOmCgoKCILc8MnGObYTJz89HYWEhunfv7t7mcDiQk5ODdevWYfjw4WFsHRFFJN4ShoiIKPoE8XY/LpcL//nPf5CVlYWysjK88sorWLt2LfLy8jBp0iS88847HsevWbNGs5ycnBy/6o0kDGwjTGFhIQAgLS3NY3taWhr27Nmje15JSQlKSkrcz4uKigAAsfGxiIvhyxxqcfGxHv9SaLG/rcX+thb721rsb+uxz63F/raQywWcMX+4mdv9yJ/pZQ6HAw6Hw+v4efPm4aabbsLLL7+MXbt2oWnTprDb7Wjfvj0ef/xxr+OfeOIJ98/FxcX45Zdf0KRJE2zdutX8BUQYvsMjlKReCVQIr21KU6dOxcSJE722D32rNxITE4PePtI29K3e4W7CBYX9bS32t7XY39Zif1uPfW4t9nfonT59GssHf2j+BKO5tOe2Z2VleWyeMGECcnNzPbY5nU58/PHHWLhwIV5++WUcO3YMKSkpHvvVNm7c6PF8+/btmDFjhvm2RyAGthEmPT0dwNnMbUZGhnv7wYMHvbK4SmPHjsXo0aPdz4uKipCVlYXZIxcjLsYeugYTgLPfgg59qzdmj1yMsuLycDen0mN/W4v9bS32t7XY39Zjn1uL/W2dMlepX8ebydgWFBR4BKla2doPPvgAt9xyC2LO3eO9WrVqHplem83msy2XX345/vvf//rR+sjDwDbC1K9fH+np6VixYgVatmwJACgtLcWaNWswbdo03fP0hiWUF5cDEtcIs0pZcTnKzvCPhlXY39Zif1uL/W0t9rf12OfWYn+HXrnws39NzLFNSUnxCGy17Ny5E1u3bsUHH3yAXbt24Z133sHOnTtRWlqKvLw8NG/e3OucOXPmuH92Op3YsmUL7PboToYxsA2DkydP4tdff3U/z8/Px7Zt21C9enXUq1cPo0aNwpQpU9CoUSM0atQIU6ZMQWJiIgYPHhzGVhMRERERUbCYydiaoUx+tWrVCq+++irmz5+PnJwcxMfHY+7cuV7nLF682P1zcXExtm3bhi+//NJ8pRGIgW0YbNq0CZ07d3Y/l4cQDxkyBLNnz8aYMWNw5swZjBgxAkePHkXr1q2xfPlyJCcnh6vJREREREQUTCbm2Ppr06ZNAICBAwdi4MCBusd9/PHHHs/379+PRx99FPPnzw+s4gjAwDYMOnXqBGGwhLckScjNzfWaGE5ERERERJVDsDK2wZCRkYHt27dbW2mQMbAlIiIiIiKymkucfejtCyHl3VScTic2b96MunXrhrTOUGNgS0REREREZLUQDEX2pbi4GPHx8Th16pR7W2xsLPr16xf16/kwsCUiIiIiIrKYBIOhyCGqs3Xr1vj+++/x4osvhqiG8GFgS0REREREZDUTt/sJtlq1auHBBx9Ey5YtERurHwoOGTIkJPWHEgNbIiIiIiIii4Vj8aj58+dj5syZWLNmDYqLizWPEUIwsCUiIiIiIqLIVLNmTTz77LPhbkZIxIS7AURERERERBcc4eMRAgcPHvR5zIEDB0JTeYgxsCUiIiIiIrKYJIThIxRWrlyJa665BnPnzvUIcsvLy7FhwwaMGjUKffv2DUndocbAloiIiIiIyGouH48QGDhwIP75z39i/fr1aN68OWrUqIE6deogNTUVjz32GFq2bIl169aFpvIQ4xxbIiIiIiIiixllZkOVsQWASy65BDNnzsTMmTOxf/9+nDlzBhkZGUhISAhZnVZgYEtERERERGQ1o7m0oYtrPWRkZFhTkQU4FJmIiIiIiMhq8n1s9R4h8M9//hP/+Mc/cPr0ac39W7duxc033xySukONGVsiIiIiIiKLheM+trfddhteeOEFNGnSBE2aNMFll12G+Ph4FBYWYv369UhLS8PUqVNDU3mIMWNLRERERERktTBkbBMSEjBx4kT8/PPPePDBB1GzZk3YbDa0bt0aX3zxBVatWoU2bdqEpO5QY8aWiIiIiIjIYpLr7ENvXyjFx8ejV69e6NWrV2grshADWyIiIiIiIqsZZWZDuCpyZcXAloiIiIiIyGoRsCpyZcLAloiIiIiIyGLhuo9tZcXAloiIiIiIyGocihxUDGyJiIiIiIisJgDoLRLFuNZvDGyJiIiIiIgsxqHIwcX72BIREREREVlNwOA+tuaL2bx5Mzp27IicnBzccsstKCsrw0cffYS2bduiS5cuKCgoCNklRBIGtkRERERERFbTDWoN5t5qqFOnDpYtW4Y1a9bg4osvxsKFC/HKK69gzZo1mDRpEiZNmhTCi4gcDGwjUG5uLiRJ8nikp6eHu1lERERERBRh0tPTkZiYCACIi4vD//73PzRt2hR2ux3t27fH9u3bw9xCa3CObYRq2rQpvvrqK/dzm80WxtYQERERXSAkiSvSkjVcACSDfQCKioo8NjscDjgcDs1T9u7di6+++gpTpkzBX3/95d7udDqD0NjIx8A2QsXGxjJLS0RERBQq5wJYKTYWwul0B7NSXNzZ/S4XRHl5GBtIlZ2ZxaOysrI8tk+YMAG5ublexxcVFeGOO+7ArFmz4HQ6PQLiCyVBxsA2Qu3atQuZmZlwOBxo3bo1pkyZggYNGoS7WURERESVjhTvgCguAYSAKCs7uy2GM/YoxEzcx7agoAApKSnuzVrZWqfTidtuuw3jx4/HJZdcgrKyMuzcuROlpaXIy8tD8+bNQ9L8SMPANgK1bt0ac+fOxSWXXIIDBw5g8uTJaNeuHXbs2IEaNWponlNSUoKSkhL3c/lbmtj4WMTF8GUOtbj4WI9/KbTY39Zif1uL/W0t9rf1IrPPy4F4raxWJLUxMJHZ35WUywWc8eN4E4FtSkqKR2Cr5eOPP8a6detw4sQJTJo0CQ888ABGjRqFnJwcxMfHY+7cuX40KnpJQnASQaQ7deoUGjZsiDFjxmD06NGax+Tm5mLixIle2+fNm+eeTE5ERERERKFx+vRpDB48GMePHzcMRouKipCamoprL3sMsTbt+bLlzhJ8/dPLPsui8/jVTRRISkrC5Zdfjl27dukeM3bsWI+gt6ioCFlZWZg9cjHiYuxWNPOCFhcfi6Fv9cbskYtRVsz5OKHG/rYW+9ta7G9rsb+txz63FvvbOmWuUv9OMLF4FJnHwDYKlJSU4KeffkLHjh11j9FbIa28uByQOEfEKmXF5Sg7wz8aVmF/W4v9bS32t7XY39Zjn1uL/R165cK//jWzeBSZx4gnAj3++ONYs2YN8vPzsWHDBtx0000oKirCkCFDwt00IiIiIiIKBnmOrd6D/MKMbQT6448/MGjQIBw6dAi1atVCmzZt8N133yE7OzvcTSMiIiIiomBwCUDSCWBdDGz9xcA2As2fPz/cTSAiIiIiolAysSoymcfAloiIiIiIyHJGQ44Z2PqLgS0REREREZHVmLENKga2REREREREVnMJ6GZmOcfWbwxsiYiIiIiIrCZcZx96+8gvDGyJiIiIiIisxqHIQcXAloiIiIiIyGocihxUMeFuABEREREREVFFMGNLRERERERkNQ5FDioGtkRERERERFYTMAhsLW1JpcDAloiIiIiIyGrM2AYVA1siIiIiIiKruVwAdG7r4+LtfvzFwJaIiIiIiMhqzNgGFQNbIiIiIiIiqzGwDSoGtkRERERERFbjfWyDivexJSIiIiKKRJJ0/kebzXC/qeLMlqHa5j5PuV2SjOs/t0+zTgIACOEyfJB/GNgSEREREUUgn0Gh0XBVk0GvFBPjfayqXOF0erfHaBjtuXIZ1PogxNnMrNaDQ5H9xsCWiIiIiCgCifLy8z+fCy4NyVlSu127PGUZ544VytV3fQTDyvZ4VOtwaNYlXC5z7b5QyV8O6D3ILwxsiYiIiIgqwo8hwSHNYgpxti1Op3dgpJeVVQZR/gZTcnBcUqLfHtLnchk//PD444+jY8eOuO2221BaWhqiBkc2BrZERERERIGSJECIswGriQDXbAbTVACsU5+7DuV+X0Gmn/N13WUGcl6grKzLCkHK2G7duhWFhYVYu3YtmjRpgv/85z8hbHTkYmBb2ckT+wN56J2vVQcRERFVHvzb7jetYDIo2Vm9z17qhZzcDTkfEEkxMYqfKzBfV68dUSJS5/oKl8vwYdb69evRvXt3AECPHj2wbt26UDU5ovF2P6RP7z8wo+BW/uZO/s9R62flNqso65ZxeAwREZEmKSaGcyMDpOw7f4IT9ecj+Vz1ayE5HEB5+fnjbTYIo6Gn8nBh4dL/DKbernVcJNxvVd1GeZtWexXbvN7L4fgsqkUY3O7nXPuKioo8NjscDjhUc5qPHTuGzMxMAEBqaiqOHDkS9KZGA2ZsKbjUmV6tn5XbrHoo61a21Vc2moiI6EIjSWcDgcr4d1Hrs4D8o83mNZzYK9MnL84UG3t+n9YKwsqgyyydY9VBmSgpcS/KJJxOiLIy/SJdLkiS6uO+1usqD6XGuYWnghH06Xy20upTd7+fe7jbotqu+ZnOYJvXbYqMPhdWVCDl6a2ILD8AZGVlITU11f2YOnWqVzHVqlVzB8DHjh1D9erVK3Qp0YoZW4puWplhJb3/ZCLhWzoiIqJIozeyySg75itzFmg7zAyN1fscoP77r7c4kuJ8d3ZVmTVVZ/rkDKDO6sABL8LkL2X5PuryuB+qwbHC5YJksxkGyh58ZEk161P2s3w8vDPbwuXyPlZ1jm6bVOVIsbEQTqf7X9NluQvRmEesNULRbHnqcqCT1T9XdkFBAVJSUtyb1dlaAGjTpg1efvll3HnnnVi2bBnat2/vXzsqCWZsI9iMGTNQv359xMfH48orr8TatWuDX4nVAV4w6tOaUG80PIbLpxMRUbTQmzdpZpvR6CONUUpe2Sxfzv39lOdtKjNv7rmcwQpqtZ77EzT4u9JvZf184Gc/CJer4v1QkaxoCF4H97DwQIfWB/o7FyQpKSkeD63AtmXLlkhPT0fHjh2xc+dODBgwIKRtilTM2Eaojz76CKNGjcKMGTPQvn17/OMf/0DPnj2xc+dO1KtXz3xB8th9o7ml0RjcBrMcIiIiPYEMKZWHQcbEnM0YnftXuc19qHyMzeb+4C3ZbJrnKYcIy/Mu3ceeCzLlDJW7zRoZODkIlbNicp3u8tXXrrz+c8fI9cpZK/c1KNrtca16o6rMZGZ9ZVv1yjfa7quuSBCOtlR06LQyu6m3Hzj/uutl+Y3mzWrVr96uN4ogwobXC5eAkHSGoPv5+k+fPj0YTYpqzNhGqFdeeQV333037rnnHlx22WV47bXXkJWVhZkzZwZWILOVRERUGfiYZ6eck6f+WfsRe+68WEh2+9ltsbHnHzExZ7fJ++SHvP9cHcqfAe2gFoBXoOc+RhFQyseoz/M6RjFUU7mKqnvupct1PqhVfA6Q9yt5lK8MJNWfHRTt9ghMztUrl6/81+eoKrPbKbSCmXgw+2WFUb1G7w2tQFidndb6QkRvJF8wH/4QLuMH+YWBbQQqLS3F5s2b3ct2y7p3737BLt9NRERRyo+hsXrDZT0WkAHcwSaAswGnHICey1TK++Usom5wCUC4zgdzoqzMc1EeOTg8t0CPXvCotYKwVnAq16P+oO0RJCqP8edDs96xZgLLQD6YawULROQX4RKGD/IPhyJHoEOHDsHpdCItLc1je1paGgoLCzXPKSkpQUlJifv58ePHz/7gEIDEb3xCzu7C6dOnAbsL8GdpfwoM+9ta7G9rsb89CHjPi1NuE3ACereotGkcq/45LhanT5+GiC0D7Obm4KnbJD/Xaitp4HvcWuxv6wgBnDE/jLhclOhmZsthchEvcpOEvwO4KeT27duHOnXqYN26dWjbtq17+/PPP49//etf+Pnnn73Oyc3NxcSJE61sJhERERERqRQUFKBu3bq6+4uLi1G/fn3dhJUsPT0d+fn5iI+PD3YTKyVmbCNQzZo1YbPZvN7sBw8e9MriysaOHYvRo0e7n7tcLhw5cgQ1atSAFGET5SujoqIiZGVleS3JTqHB/rYW+9ta7G9rsb+txz63FvvbOkIInDhxApmZmYbHxcfHIz8/H6WlpYbH2e12BrV+YGAbgex2O6688kqsWLEC/fr1c29fsWIF+vbtq3mOw+HwWv67atWqoWwmaZCXYidrsL+txf62FvvbWuxv67HPrcX+tkZqaqqp4+Lj4xm0BhkD2wg1evRo3HHHHWjVqhXatm2Ld955B3v37sX9998f7qYRERERERFFFAa2EerWW2/F4cOH8dxzz2H//v1o1qwZlixZguzs7HA3jYiIiIiIKKIwsI1gI0aMwIgRI8LdDDLB4XBgwoQJXsPBKTTY39Zif1uL/W0t9rf12OfWYn/ThYKrIhMREREREVFUiwl3A4iIiIiIiIgqgoEtERERERERRTUGtkRERERERBTVGNgSAZgxYwbq16+P+Ph4XHnllVi7dq17X25uLi699FIkJSWhWrVq6Nq1KzZs2OCzzO3btyMnJwcJCQmoU6cOnnvuOaintK9ZswZXXnkl4uPj0aBBA7z99ttBv7ZIZNTfAPDTTz/hhhtuQGpqKpKTk9GmTRvs3bvXsEz2tz6j/j5w4ACGDh2KzMxMJCYmokePHti1a5fPMtnf2r755hv06dMHmZmZkCQJCxcudO8rKyvDk08+icsvvxxJSUnIzMzEnXfeiX379vksl/2tzai/AWDo0KGQJMnj0aZNG5/lsr+1+ervkydP4sEHH0TdunWRkJCAyy67DDNnzvRZLvtb29SpU3HVVVchOTkZtWvXxo033ohffvnF45gFCxbguuuuQ82aNSFJErZt22aqbPY5VUqC6AI3f/58ERcXJ959912xc+dO8cgjj4ikpCSxZ88eIYQQH374oVixYoX47bffxI8//ijuvvtukZKSIg4ePKhb5vHjx0VaWpoYOHCg2L59u/jkk09EcnKymD59uvuY33//XSQmJopHHnlE7Ny5U7z77rsiLi5O/Oc//wn5NYeTr/7+9ddfRfXq1cUTTzwhtmzZIn777TexaNEiceDAAd0y2d/6jPrb5XKJNm3aiI4dO4qNGzeKn3/+Wdx3332iXr164uTJk7plsr/1LVmyRDz99NPik08+EQDEp59+6t537Ngx0bVrV/HRRx+Jn3/+Waxfv160bt1aXHnllYZlsr/1GfW3EEIMGTJE9OjRQ+zfv9/9OHz4sGGZ7G99vvr7nnvuEQ0bNhSrVq0S+fn54h//+Iew2Wxi4cKFumWyv/Vdd911YtasWeLHH38U27ZtE7179/b6/3nu3Lli4sSJ4t133xUAxNatW32Wyz6nyoqBLV3wrr76anH//fd7bLv00kvFU089pXn88ePHBQDx1Vdf6ZY5Y8YMkZqaKoqLi93bpk6dKjIzM4XL5RJCCDFmzBhx6aWXepw3fPhw0aZNm0AvJSr46u9bb71V3H777X6Vyf7WZ9Tfv/zyiwAgfvzxR/e+8vJyUb16dfHuu+/qlsn+Nkfrg7/axo0bBQD3Fzta2N/m6AW2ffv29asc9rc5Wv3dtGlT8dxzz3lsu+KKK8QzzzyjWw7727yDBw8KAGLNmjVe+/Lz800Htuxzqqw4FJkuaKWlpdi8eTO6d+/usb179+5Yt26d5vHvvPMOUlNT0aJFC/f2oUOHolOnTu7n69evR05Ojsc946677jrs27cPu3fvdh+jrve6667Dpk2bUFZWFoSrizy++tvlcmHx4sW45JJLcN1116F27dpo3bq15vBC9rdvvvq7pKQEABAfH+/eZ7PZYLfb8e2337q3sb9D5/jx45AkCVWrVnVvY38H1+rVq1G7dm1ccskluPfee3Hw4EGP/ezv4OnQoQM+//xz/PnnnxBCYNWqVfjf//6H6667zn0M+ztwx48fBwBUr17dr/PY53ShYGBLF7RDhw7B6XQiLS3NY3taWhoKCwvdzxctWoQqVaogPj4er776KlasWIGaNWu692dkZKBevXru54WFhZplyvuMjikvL8ehQ4eCc4ERxld/Hzx4ECdPnsQLL7yAHj16YPny5ejXrx/69++PNWvWuI9nf5vjq78vvfRSZGdnY+zYsTh69ChKS0vxwgsvoLCwEPv373cfz/4OjeLiYjz11FMYPHgwUlJS3NvZ38HTs2dPfPjhh1i5ciVefvll5OXloUuXLu4vdQD2dzC98cYbaNKkCerWrQu73Y4ePXpgxowZ6NChg/sY9ndghBAYPXo0OnTogGbNmvl1LvucLhSx4W4AUSSQJMnjuRDCY1vnzp2xbds2HDp0CO+++y5uueUWbNiwAbVr1wZwdoEHM2Wqt5s5pjLS62+XywUA6Nu3Lx599FEAwN/+9jesW7cOb7/9NnJycgCwv/2l199xcXH45JNPcPfdd6N69eqw2Wzo2rUrevbs6XE8+zv4ysrKMHDgQLhcLsyYMcNjH/s7eG699Vb3z82aNUOrVq2QnZ2NxYsXo3///gDY38H0xhtv4LvvvsPnn3+O7OxsfPPNNxgxYgQyMjLQtWtXAOzvQD344IP44YcfPEbTmMU+pwsFA1u6oNWsWRM2m80jOwsABw8e9PimMikpCRdffDEuvvhitGnTBo0aNcJ7772HsWPHapabnp6uWSZw/ltRvWNiY2NRo0aNCl9bJPLV3zVr1kRsbCyaNGnisf+yyy4z/GPO/tZm5v195ZVXYtu2bTh+/DhKS0tRq1YttG7dGq1atdItl/1dMWVlZbjllluQn5+PlStXemRrtbC/gycjIwPZ2dmGK3+zvwNz5swZjBs3Dp9++il69+4NAGjevDm2bduG6dOnuwNbNfa3bw899BA+//xzfPPNN6hbt26Fy2OfU2XFoch0QbPb7bjyyiuxYsUKj+0rVqxAu3btdM8TQngMZVNr27YtvvnmG5SWlrq3LV++HJmZmbjooovcx6jrXb58OVq1aoW4uLgAriby+epvu92Oq666yut2Bv/73/+QnZ2tWy77W5s/7+/U1FTUqlULu3btwqZNm9C3b1/dctnfgZOD2l27duGrr74y9QGR/R08hw8fRkFBATIyMnSPYX8HpqysDGVlZYiJ8fxoabPZ3KNxtLC/9Qkh8OCDD2LBggVYuXIl6tevH5Ry2edUaVm6VBVRBJJvh/Lee++JnTt3ilGjRomkpCSxe/ducfLkSTF27Fixfv16sXv3brF582Zx9913C4fD4bGS7FNPPSXuuOMO9/Njx46JtLQ0MWjQILF9+3axYMECkZKSormU/qOPPip27twp3nvvvQtiKX2j/hZCiAULFoi4uDjxzjvviF27dom///3vwmazibVr17rLYH+b56u/P/74Y7Fq1Srx22+/iYULF4rs7GzRv39/jzLY3+adOHFCbN26VWzdulUAEK+88orYunWr2LNnjygrKxM33HCDqFu3rti2bZvHLWhKSkrcZbC/zTPq7xMnTojHHntMrFu3TuTn54tVq1aJtm3bijp16oiioiJ3Gexv84z6WwghcnJyRNOmTcWqVavE77//LmbNmiXi4+PFjBkz3GWwv8174IEHRGpqqli9erXH/xenT592H3P48GGxdetWsXjxYgFAzJ8/X2zdulXs37/ffQz7nC4UDGyJhBBvvfWWyM7OFna7XVxxxRXupfTPnDkj+vXrJzIzM4XdbhcZGRnihhtuEBs3bvQ4f8iQISInJ8dj2w8//CA6duwoHA6HSE9PF7m5ue5l9GWrV68WLVu2FHa7XVx00UVi5syZIb3OSKHX37L33ntPXHzxxSI+Pl60aNHC6x6I7G//GPX366+/LurWrSvi4uJEvXr1xDPPPOMRZAnB/vbHqlWrBACvx5AhQ9y349B6rFq1yl0G+9s8o/4+ffq06N69u6hVq5b7/T1kyBCxd+9ejzLY3+YZ9bcQQuzfv18MHTpUZGZmivj4eNG4cWPx8ssve/Qd+9s8vf8vZs2a5T5m1qxZmsdMmDDBfQz7nC4UkhDnZoITERERERERRSHOsSUiIiIiIqKoxsCWiIiIiIiIohoDWyIiIiIiIopqDGyJiIiIiIgoqjGwJSIiIiIioqjGwJaIiIiIiIiiGgNbIiIiIiIiimoMbImIiIiIiCiqMbAlIiIiIiKiqMbAloiIiIiIiKIaA1siIiIiIiKKagxsiYiIiIiIKKoxsCUiIiIiIqKoxsCWiIgs9cMPP2DYsGGoX78+4uPjUaVKFVxxxRV48cUXceTIEfdxnTp1QqdOncLSxtzcXEiS5PO4Tp06oVmzZha0KHiGDh2KKlWqBLXMirxWZttz+vRp5ObmYvXq1QHVQ0RElVtsuBtAREQXjnfffRcjRoxA48aN8cQTT6BJkyYoKyvDpk2b8Pbbb2P9+vX49NNPw91M8tOMGTNCXsfp06cxceJEAAjbFx5ERBS5GNgSEZEl1q9fjwceeADdunXDwoUL4XA43Pu6deuGxx57DEuXLg1jCylQTZo0CXcTiIjoAsehyEREZIkpU6ZAkiS88847HkGtzG6344YbbjAs48iRIxgxYgTq1KkDu92OBg0a4Omnn0ZJSYn7mN27d0OSJMyePdvrfEmSkJub67Ft8eLF+Nvf/gaHw4H69etj+vTpfl/b2rVr0aZNGyQkJKBOnTp49tln4XQ6PY6ZOHEiWrdujerVqyMlJQVXXHEF3nvvPQghPI5buXIlOnXqhBo1aiAhIQH16tXDgAEDcPr0afcxpaWlmDx5Mi699FI4HA7UqlULw4YNw19//WW6zb/++it69eqFKlWqICsrC4899phHP/pTj9ZQ5D/++AM33XQTkpOTUbVqVdx2223Iy8vTfW2M2rN7927UqlXL3Y+SJEGSJAwdOtT09RIRUeXGjC0REYWc0+nEypUrceWVVyIrKyugMoqLi9G5c2f89ttvmDhxIpo3b461a9di6tSp2LZtGxYvXux3mV9//TX69u2Ltm3bYv78+XA6nXjxxRdx4MAB02UUFhZi4MCBeOqpp/Dcc89h8eLFmDx5Mo4ePYo333zTfdzu3bsxfPhw1KtXDwDw3Xff4aGHHsKff/6J8ePHu4/p3bs3OnbsiPfffx9Vq1bFn3/+iaVLl6K0tBSJiYlwuVzo27cv1q5dizFjxqBdu3bYs2cPJkyYgE6dOmHTpk1ISEgwbHNZWRluuOEG3H333XjsscfwzTffYNKkSUhNTXW3pSL1nDp1Cp07d8aRI0cwbdo0XHzxxVi6dCluvfXWgNqTkZGBpUuXokePHrj77rtxzz33AIA72CUiIoIgIiIKscLCQgFADBw40PQ5OTk5Iicnx/387bffFgDExx9/7HHctGnTBACxfPlyIYQQ+fn5AoCYNWuWV5kAxIQJE9zPW7duLTIzM8WZM2fc24qKikT16tWFmT+ROTk5AoD47LPPPLbfe++9IiYmRuzZs0fzPKfTKcrKysRzzz0natSoIVwulxBCiP/85z8CgNi2bZtunf/+978FAPHJJ594bM/LyxMAxIwZMwzbPGTIEM1+7NWrl2jcuHFA9ahfq7feeksAEF9++aXHucOHD/d6bcy256+//vJ6/YiIiGQcikxERFFh5cqVSEpKwk033eSxXR6O+vXXX/tV3qlTp5CXl4f+/fsjPj7evT05ORl9+vQxXU5ycrLXEOrBgwfD5XLhm2++8Wh/165dkZqaCpvNhri4OIwfPx6HDx/GwYMHAQB/+9vfYLfbcd9992HOnDn4/fffvepbtGgRqlatij59+qC8vNz9+Nvf/ob09HRTqwZLkuR1jc2bN8eePXuCUs+aNWuQnJyMHj16eGwfNGhQwO0hIiIywsCWiIhCrmbNmkhMTER+fn7AZRw+fBjp6elet+GpXbs2YmNjcfjwYb/KO3r0KFwuF9LT0732aW3Tk5aWpnu+3KaNGzeie/fuAM6uDP3f//4XeXl5ePrppwEAZ86cAQA0bNgQX331FWrXro2RI0eiYcOGaNiwIV5//XV32QcOHMCxY8dgt9sRFxfn8SgsLMShQ4d8tjkxMdEjmAcAh8OB4uLioNRz+PBhzX7R2ma2PUREREY4x5aIiELOZrPh2muvxZdffok//vgDdevW9buMGjVqYMOGDRBCeAS3Bw8eRHl5OWrWrAkA7gBJvRCSOvCtVq0aJElCYWGhV11a2/RozceVz69RowYAYP78+YiLi8OiRYs8AriFCxd6nduxY0d07NgRTqcTmzZtwt///neMGjUKaWlpGDhwIGrWrIkaNWroriCdnJxsuu1GKlJPjRo1sHHjRq/t/vQrERGRP5ixJSIiS4wdOxZCCNx7770oLS312l9WVoYvvvhC9/xrr70WJ0+e9AoG586d694PnM0KxsfH44cffvA47rPPPvN4npSUhKuvvhoLFizwyAyeOHHCsB1qJ06cwOeff+6xbd68eYiJicE111wD4OxQ29jYWNhsNvcxZ86cwb/+9S/dcm02G1q3bo233noLALBlyxYAwPXXX4/Dhw/D6XSiVatWXo/GjRubbruRitSTk5ODEydO4Msvv/TYPn/+/IDbI6+kLWe3iYiIlJixJSIiS7Rt2xYzZ87EiBEjcOWVV+KBBx5A06ZNUVZWhq1bt+Kdd95Bs2bNdOe33nnnnXjrrbcwZMgQ7N69G5dffjm+/fZbTJkyBb169ULXrl0BnA0ib7/9drz//vto2LAhWrRogY0bN2LevHleZU6aNAk9evRw30fX6XRi2rRpSEpKwpEjR0xdV40aNfDAAw9g7969uOSSS7BkyRK8++67eOCBB9wrIPfu3RuvvPIKBg8ejPvuuw+HDx/G9OnTvW579Pbbb2PlypXo3bs36tWrh+LiYrz//vsA4L6+gQMH4sMPP0SvXr3wyCOP4Oqrr0ZcXBz++OMPrFq1Cn379kW/fv3MvSgGKlLPkCFD8Oqrr+L222/H5MmTcfHFF+PLL7/EsmXLAAAxMf5/r56cnIzs7Gx89tlnuPbaa1G9enXUrFkTF110UUUuk4iIKotwr15FREQXlm3btokhQ4aIevXqCbvdLpKSkkTLli3F+PHjxcGDB93HqVfaFUKIw4cPi/vvv19kZGSI2NhYkZ2dLcaOHSuKi4s9jjt+/Li45557RFpamkhKShJ9+vQRu3fv1lxV9/PPPxfNmzcXdrtd1KtXT7zwwgtiwoQJpldFbtq0qVi9erVo1aqVcDgcIiMjQ4wbN06UlZV5HPv++++Lxo0bC4fDIRo0aCCmTp0q3nvvPQFA5OfnCyGEWL9+vejXr5/Izs4WDodD1KhRQ+Tk5IjPP//co6yysjIxffp00aJFCxEfHy+qVKkiLr30UjF8+HCxa9cuwzYPGTJEJCUleW3Xumaz9Wi9Vnv37hX9+/cXVapUEcnJyWLAgAFiyZIlXqtI+9Oer776SrRs2VI4HA4BQAwZMsTwWomI6MIhCaG6MzwRERFRCEyZMgXPPPMM9u7dG9A8ayIiIj0cikxERERB9+abbwIALr30UpSVlWHlypV44403cPvttzOoJSKioGNgS0REREGXmJiIV199Fbt370ZJSQnq1auHJ598Es8880y4m0ZERJUQhyITERERERFRVOPtfoiIiIiIiCiqMbAlIiIiIiKKYqtXr8a1116LnJwcfPbZZ/joo4/Qtm1bdOnSBQUFBeFuniU4FJmIiIiIiChKFRcX4+abb8Ynn3wCu92OsrIydOjQAWvXrkVeXh7mzJmDd955J9zNDDkuHlVJuVwu7Nu3D8nJyZAkKdzNISIiIiKq1IQQOHHiBDIzMxETYzwwtri4GKWlpT7LU3+OdzgccDgcHtvWrVuHhIQE9OnTB4mJiXjiiSfQtGlT2O12tG/fHo8//nhgFxRlGNhWUvv27UNWVla4m0FEREREdEEpKCgwvK1ZcXEx6mdXQeFBp2E5VapUwcmTJz22TZgwAbm5uR7bDhw4gPz8fPz3v//F119/jdzcXDRp0sS93+k0rqeyYGBbSSUnJwMAOqAXYhEX5tZUfrEJcbjrvf54/+7/Z+/O46Mq7/7/v885M5kkQFJFKyhxqagIirK4UISwK3Vfqmjrir37rdpbau1itRXk9ka7oPddK4oilt9dKta91VsFZHMPm4LUfSF3FXElLElm5pzr98dkJplkJsnAyZlJeD0fj/OYmetc5zqfc83kTD5zneURxWtj+Q6ny6O/g0V/B4v+Dpaf/R3q3UvxTzb5FFnXxWc8WPR3cOKK6Xk9lfo/PJtoNKpNm119sOoAlfXIPLJbs9XTQUM+UnV1tcrKylLlzUdrJekb3/iGTjjhBBUVFWnMmDG66KKL0hJrx3F2cos6FxLbLip52EJIYYUsEtuOFrbCKi0tVdgKSxz53eHo72DR38Giv4PlZ3+H7CKJ79w28RkPFv0doIYrF7X3NMBu3RNTJm5DW2VlZWmJbSbHHnusbr/9dknSmjVrNGHCBG3YsEHRaFRVVVUaOHBgu+Lp7EhsAQAAACBgnow8Zb6Ob7byTHr27KnTTjtNI0eOlG3buu+++/Tqq6+qsrJSxcXFmjdvnl8hFzQSWwAAAAAImCdPXivzcnHllVfqyiuvTL3+1re+pUmTJu1CdJ0PiS0AAAAABMw1Rm6WO69mK0d2JLYAAAAAEDC/DkVGQus3WAIAAAAAoMAxYgsAAAAAAfNk5DJi6xsSWwAAAAAIGIci+4vEFgAAAAACxsWj/EViCwAAAAAB8xqmbPOQGxJbAAAAAAiY28o5ttnKkR2JLQAAAAAEzDWJKds85IbEFgAAAAACxqHI/iKxBQAAAICAebLkyso6D7khsQUAAACAgHkmMWWbh9yQ2AIAAABAwNxWRmyzlSM7ElsAAAAACBiJrb9IbAEAAAAgYJ6x5Jks59hmKUd2JLYAAAAAEDBGbP1FYgsAAAAAAXNly5WdZR5yRWILAAAAAAEzrRyKbDgUOWeZfyJAQZkxY4Ysy9KUKVPyHQoAAAAAHyQPRc42ITcktgWuqqpKs2fP1sCBA/MdCgAAAAAUJBLbArZt2zZ973vf0z333KM99tgj3+EAAAAA8Ilr7FYn5IZzbAvYlVdeqZNPPlnjxo3Tf/zHf7Rat76+XvX19anXNTU1kqRQSVhhK9yhcUIKl4TSHtGx6O9g0d/Bor+D5Wd/h4odWSV857aFz3iw6O8AGUm17a/uyZKXZZzRk/Enpt2IZYyh1wrQAw88oJtvvllVVVUqLi7WqFGjdPTRR+v222/PWH/q1KmaNm1ai/L58+ertLS0g6MFAAAAdm87duzQBRdcoC1btqisrCxrvZqaGpWXl+uJ1w9Wtx5Oxjrbt7o6beB7bbaFRvx0U4Cqq6t19dVX69lnn1VxcXG7lrnuuut0zTXXpF7X1NSooqJC901+hBHbAIRLQrpsztm6b/LDitXG8x1Ol0d/B4v+Dhb9HSw/+zvUex/FP/nUp8i6Lj7jwaK/gxMzsZzqt3bIscvYY85IbAvQqlWrtHnzZg0ZMiRV5rquli9frjvuuEP19fVynPRfdyKRiCKRSIu24rUxcVG14MRq44rV5rZTw86jv4NFfweL/g6WH/1t6tzE9y7ahc94sOjvjhfPMbFNHIqc+R/1bOXIjsS2AI0dO1br1q1LK7v00kvVr18//eIXv2iR1AIAAADoXDzZcjnH1jcktgWoR48eOuKII9LKunXrpp49e7YoBwAAAND5cCiyv0hsAQAAACBgnmyuiuwjEttOYunSpfkOAQAAAIBPXGPJNZnPpc1WjuxIbAEAAAAgYG4r59i6jNjmjMQWAAAAAALmGVtelnNsPc6xzRmJLQAAAAAEjBFbf5HYAgAAAEDAPGU/l9YLNpQugcQWAAAAAALW+lWRM5cjOxJbAAAAAAhY6/exJbHNFYktAAAAAATMkyVP2Q5F5nY/ueKnAAAAAABAp0ZiCwAAAAABSx6KnG1qrw8//FB77723Ro0apVGjRumzzz7TggULNGzYMI0ZM0bV1dUduBWFg0ORAQAAACBgrd/uJ7fxx8rKSj300EOSpFgsppkzZ2rFihWqqqrS9OnTNXv27F2Ot9AxYgsAAAAAAfOM1eokSTU1NWlTfX19xrZeeOEFjRgxQr/61a/09ttva8CAASoqKtLw4cO1bt26IDcrb0hsAQAAACBgXsOIbaYpebufiooKlZeXp6YZM2a0aKd379569913tXz5cm3evFmPP/64ysrKUvNd1w1sm/KJQ5EBAAAAIGCeseVlOZc2WV5dXZ2WpEYikRZ1I5FIqvzss8/Wfffdpx49eqTmO47jZ9gFixFbAAAAAAiYK6vVSZLKysrSpkyJ7datW1PPly9frlNOOUUbNmxQNBrVCy+8oIEDBwa2TfnEiC0AAAAABKw9I7bt8fzzz+uGG25QaWmpDjroIE2fPl2RSESVlZUqLi7WvHnz/Aq5oJHYAgAAAEDAXCk1MptpXntNnDhREydOTCubNGmSJk2atPPBdUIktgAAAAAQML9GbJFAYgsAAAAAAXONLTdLAputHNmR2AIAAABAwIwseVkORTZZypEdiS0AAAAABIwRW3+R2AIAAABAwDxjyTOZR2azlSM7fgoAAADwQfzjTxTat3e+wwDQSbiyW52QG3oMANDlxU48JrD1uKOHtGu9zefFxw+VJLmjB6U9Jssz1UVhcfbcQ/GPP8l3GAA6ieSIbbYJueFQZAAAAAAImCdbXpZxxmzlyI4eAwAAAAB0aozYAgAAAEDAXGPJzXLIcbZyZEdiCwAAAAAB46rI/iKxBQAAAICAGWPLy3K/WsN9bHNGYgsAAAAAAXNlyVWWQ5GzlCM7ElsAAAAACJhnsh9y7JmAg+kCSGwBAAAAIGBeK4ciZytHdiS2AAAAABAwT5a8LIccZytHdiS2AAAAABAwbvfjLxJbAAAAAAgYhyL7i8QWAAAAAALmqZX72HIocs5IbAEAAAAgYKaVc2wNiW3OSGwBAAAAIGCeaWXElnNsc0ZiCwAAAAAB4xxbf5HYAgAAAEDAGLH1F4ktAAAAAASM+9j6i8QWAAAAAALGiK2/SGwBAAAAIGAktv7irGQAAAAAQKdGYluAZs2apYEDB6qsrExlZWUaNmyY/vd//zffYQEAAADwSXLENtuE3JDYFqA+ffrolltu0cqVK7Vy5UqNGTNGp59+ut544418hwYAAADAByS2/uIc2wJ06qmnpr2++eabNWvWLL388ssaMGBAnqICAAAA4Bej7Fc/NsGG0iWQ2BY413X1t7/9Tdu3b9ewYcPyHQ4AAAAAH3DxKH+R2BaodevWadiwYaqrq1P37t316KOPqn///lnr19fXq76+PvW6pqZGkhQqCStshTs83t1duCSU9oiORX8Hqyv0txW2FSrp+H2hFbZlS7Ib1tXaepvPs8O2nJKw7HDiLKFw2JbX8Npp1kamMuwcPz/fTnEo9d4ju66wT+lM6O8AGUm17a9OYusvyxjDSHcBikaj2rhxo77++ms9/PDDuvfee7Vs2bKsye3UqVM1bdq0FuXz589XaWlpR4cLAAAA7NZ27NihCy64QFu2bFFZWVnWejU1NSovL9fIv1+hULdIxjrx7fVafuqdbbaFRiS2ncS4ceN08MEH6+677844P9OIbUVFhcaVnMOIbQDCJSFdNuds3Tf5YcVq4/kOp8ujv4PVFfo7PnawQotXB7IeO+bJXr62zfU2n+eOHiRnyRrZE4boB5f01T33vyvv2VWp8qYylWHn+Pn5dvb4htyvvvYnsC6sK+xTOhP6OzgxE9Oi2ofandie8MSVrSa2z5/2JxLbHHBMQidhjElLXJuLRCKKRFr+YcRrY8pyTjo6QKw2rlhtLN9h7Dbo72B15v6OxTyZAGKPxTzZUU9Ow7paW2/zefGYp1BtTHbMS833amOp8qYylWHX+PH59kricnlf2q0z71M6I/q748VNbv1rjCWT5ZDjbOXIjsS2AP3qV7/SxIkTVVFRoa1bt+qBBx7Q0qVL9fTTT+c7NAAAAAA+8GRlvSpytnJkR2JbgD799FNdeOGF+uSTT1ReXq6BAwfq6aef1vjx4/MdGgAAAAAfcPEof9n5DgAtzZkzRx9++KHq6+u1efNmLVq0iKQWAAAA6EKShyJnm3L117/+VXvvvbckacGCBRo2bJjGjBmj6upqv0MvSCS2AAAAABCw5IhttimntjxPDz30kCoqKhSLxTRz5kwtW7ZM06dP1/Tp0ztoCwoLiS0AAAAABMzPEdv58+frnHPOkW3beueddzRgwAAVFRVp+PDhWrduXQdtQWEhsQUAAACAgJlWRmuTiW1NTU3alOkuKa7r6sEHH9R5550nSfr666/TbhHkum4wG5RnJLYAAAAAEDAjyZgsU0OdiooKlZeXp6YZM2a0aOd//ud/dO6558q2E6ndHnvsoZqamtR8x3EC2Jr846rIAAAAABAwT5asNm73U11dnTb6GolEWtTdsGGD1qxZo//5n//RO++8o9mzZ2vDhg2KRqOqqqrSwIEDO2YDCgyJLQAAAAAUoLKysrTENpNbb7019Xzo0KG67bbb9MADD6iyslLFxcWaN29eR4dZEEhsAQAAACBgrV0kamdu9yNJK1eulCRNmjRJkyZN2unYOiMSWwAAAAAImGcsWVkS2Fxv9wMSWwAAAAAIXPJCUdnmITcktgAAAAAQsI44FHl3RmILAAAAAAEjsfUXiS0AAAAABIxzbP1FYgsAAAAAAeMcW3+R2AIAAABAwBKJbbZDkQMOpgsgsQUAAACAgHGOrb9IbAEAAAAgYKZhyjYPuSGxBQAAAICAMWLrLxJbAAAAAAgaQ7a+IrEFAAAAgKC1MmIrRmxzRmILAAAAAAHjdj/+IrEFAAAAgIBxjq2/SGwBAAAAIGjGyn7IMYltzux8BwAAQEcLP1Ol2InHBLIer8iWO3pIm+ttPi+0cKXi44fKWbImVRYfPzRV3lSmMuSf++VXcvbcI99hAMBuiRFbAEBBSyZ3O7tc7MRjZHm5n6zkjk0kp87iVanXyedp9RqS2CSn3pW9dHVacpuJN2pwKrm1PCM75kmSzIijJEl23EvVa57IJrdnZ/sGHcMuLZX75Vf5DgPtEJswVOFnd4+/Hadbt5yXab6/80YNlr10tZ9hQZxj6zcSWwAAAAAIGrf78RWHIncw13W1du1affUVv+ACAAAASEhePCrbhNyQ2PpsypQpmjNnjqREUltZWanBgweroqJCS5cuzW9wAAAAAAqHyTIhZyS2PnvooYd01FGJ86P+/ve/64MPPtCbb76pKVOm6Prrr89zdAAAAAAKASO2/iKx9dnnn3+uXr16SZKeeuopffe739Whhx6qyZMna926dXmODgAAAEBByDZay6jtTiGx9dk+++yjDRs2yHVdPf300xo3bpwkaceOHXIcJ8/RAQAAACgMVhsTcsFVkX126aWX6txzz1Xv3r1lWZbGjx8vSXrllVfUr1+/PEcHAAAAoCBwVWRfkdj6bOrUqTryyCO1ceNGffe731UkEpEkOY6jX/7yl3mODgAAAEBBILH1FYmtj2KxmCZMmKC7775bZ599dtq8iy++OE9RAQAAACg4xkpM2eYhJyS2PgqHw1q/fr0siw8iAAAAgOyMSUzZ5iE3XDzKZxdddFHqPrYAAAAAkBFXRfYVI7Y+i0ajuvfee7Vw4UINHTpU3bp1S5s/c+bMPEUGAAAAoGBwKLKvSGx9tn79eg0ePFiS9Pbbb6fN4xBlAAAAAJJkmcSUbZ4ftm3bpvvvv18PP/ywXn/9dW3fvl19+vTRqFGj9MMf/lDHHHOMPysqACS2PluyZEm+QwAAAABQ6Dr4qsjPPfecpkyZopNOOkk33nij+vXrp5KSEm3atEkvvviirrvuOpWXl+vhhx/e9ZUVABLbDvLuu+/qvffe08iRI1VSUiJjDCO2AAAAABI6+FDkXr166ZVXXlFJSUlaeXl5uQ477DBdeumleuWVV3Z5PYWCi0f57IsvvtDYsWN16KGH6jvf+Y4++eQTSdLll1+un/70p3mODgAAAMDuoH///i2S2uaOO+64gKLpeIzY+uwnP/mJwuGwNm7cqMMPPzxVft555+knP/mJ/vCHP+QxOgAAAAAFoYMPRU667LLLZFq5f9DcuXP9W1kekdj67Nlnn9UzzzyjPn36pJUfcsgh+uijj/IUFQAAAICCElBiO3ToUEnS6tWrtXbtWl122WX+NV5ASGx9tn37dpWWlrYo//zzzxWJRPIQEQAAAICCE1Bie8UVV+jNN9/U7bffrr322kt77rmnJk2a5N8KCgTn2Pps5MiRmjdvXuq1ZVnyPE+/+93vNHr06DxGBgAAAKBgJC8elW3yyfvvv6/TTjtNc+fO1TPPPKPf/e53WrFihW/tFwpGbH32u9/9TqNGjdLKlSsVjUb185//XG+88Ya+/PJLvfDCC+1qY8aMGXrkkUf05ptvqqSkRN/+9rd166236rDDDuvg6AEAAAAEIYj72ErSKaecojvvvFPDhw+XJD322GOaOHGi1q9f799KCgAjtj7r37+/Xn/9dR177LEaP368tm/frrPOOktr1qzRwQcf3K42li1bpiuvvFIvv/yyFi5cqHg8rgkTJmj79u0dHD0AAACAQJg2pnZav369hg8frsrKSp188snatm2bFixYoGHDhmnMmDH66U9/qnHjxqXqV1RUpB1h2lUwYtsBevXqpWnTpu308k8//XTa67lz5+qb3/ymVq1apZEjR+5qeAAAAAC6iMMOOyx1ZOi0adP06KOP6o477tCKFStUVVWlO+64Iy2xlaSePXvmI9QORWLrswMPPFCXXXaZLr30UlVUVPjS5pYtWyRJe+65Z9Y69fX1qq+vT72uqamRJIVKwgpbYV/iQHbhklDaIzoW/R2sfPe3HbbllOS+H0suZ4VtWZ6RsS2FcmjHCScOarIblnHCdup5Wr2i9IOfLGNklYRT5ZmWkSRTZMtqEp9tSV7IVrjhtKpw2JZnJNuSrJKw7HDjepLbI2mn+gaN/Px82yUheYb3oy353qdIkhW2c9ofdGbh4tz7u/n+Lrm/QhuMpNr2V7fUyqHIDY/J/+mTIpFIiwvShsON782OHTu0//77a8CAASoqKtLw4cM1atQoLVq0SJZlqb6+Xlu3blXPnj312WeftT/YTsAyrd3UCDn74x//qPvvv1+vvfaaRo8ercmTJ+vMM8/c6SsiG2N0+umn66uvvmr1JO+pU6dmHCWeP39+xqs0AwAAAPDPjh07dMEFF2jLli0qKyvLWq+mpkbl5eU64JabZRcXZ6zj1dXpo19e36L8xhtv1NSpU1uUL1y4UD//+c8VDod1++2368EHH9Ttt98uSTr22GP16quvpuo+/fTTeuGFFzR9+vTcNrDAkdh2kNdee0333Xef/vrXvyoej+uCCy7QZZddpsGDB+fUzpVXXqknn3xSzz//fIt74zaVacS2oqJC40rOYcQ2AOGSkC6bc7bum/ywYrXxfIfT5dHfwcp3f7ujB8lZsmanl4uPHdw4Yrt4dbuX9yqPliTZy9amXiefp9UbeXTaa8sYWSteS5Xby1suI0lmxFGyVryWis+Oe/JCtiKWNPnyQzXn3rdVbyQ77sla8Zrc0YMa19F0xHYn+gaN/Px826Ul8nbkMFyzm8r3PkWS4mMGKfTc7vG3U9yzXBf/8aSc+rv5/i65v0LrYiamRbUPtT+xndFGYnvd9aqurk5rK9OIbVO//e1v5bqu3nnnHd13332SpGHDhumll15Kqzd48GCtXt3+78TOgOP4OshRRx2l//qv/9Lvf/973XnnnfrFL36hWbNm6YgjjtDVV1+tSy+9VJbV+mW8f/zjH+uJJ57Q8uXLW01qpewf8nhtrPFYBnS4WG1csdpYvsPYbdDfwcpXf8djnkI7sd7kcrGYl0oETQ7tuDFPkuQ0LOPGvNTztHpRL+21ZYzs2liqPNMykuRFPdlN4rNjXurQY0mKxTxFjWTHEvXiscb1NE1sd6Zv0JIfn2/bCsvj/Wi3fO7DYzEvp/1BZ+bUJZLZXPq7+f4uub9C6+Imxz5qx31sy8rKWk2SpcQAVzIPKC8vVzQa1YYNGxSNRlVVVaW99tpLy5YtkyS5rqvVq1errq4ut1g7ARLbDhKLxfToo49q7ty5WrhwoY4//nhNnjxZH3/8sa6//notWrRI8+fPz7isMUY//vGP9eijj2rp0qU66KCDAo4eAAAAQEfy63Y/Cxcu1O9+9zvZtq29995b999/v/bee29VVlaquLhYX331lX72s59Jkurq6lRdXa1Fixb5sAWFhcTWZ6tXr9bcuXP117/+VY7j6MILL9Rtt92mfv36pepMmDCh1asbX3nllZo/f74ef/xx9ejRQ5s2bZKU+AWmpKSkw7cBAAAAQAdrx4hte5xyyik65ZRT0somTZqkSZMmZay/bt063XHHHbr77rvbv5JOgMTWZ8ccc4zGjx+vWbNm6Ywzzki7SllS//79s37QJGnWrFmSpFGjRqWVz507V5dccomf4QIAAADIB58S21wdeeSRevnllztuBXlCYuuz999/XwcccECrdbp166a5c+dmnc/1vAAAAICuza9Dkdvy5z//OfXcdV2tWrWqSx4FSmLrs7aSWgAAAACQsRJTtnk+efLJJ1PPQ6GQvvWtb+mxxx7zrf1CQWLrM9d1ddttt+nBBx/Uxo0bFY1G0+Z/+eWXeYoMAAAAQMEI6FDkBx980L/GCpid7wC6mmnTpmnmzJk699xztWXLFl1zzTU666yzZNt2xpspAwAAAIDf3n77bcVird+C6PXXXw8omo5HYuuzv/zlL7rnnnt07bXXKhQK6fzzz9e9996r3/zmN13yJG0AAAAAuUueY5tt2lXvv/++jj32WN1000169dVXtXXrVsViMVVXV2vBggU644wzdN111+36igoEia3PNm3apCOPPFKS1L17d23ZskVS4jLcTY9vBwAAALAbM21Mu+ikk07S4sWLFYlEdNVVV2nvvfdWcXGxjj32WP3973/XNddc06XyE86x9VmfPn30ySefaP/991ffvn317LPPavDgwaqqqlIkEsl3eAAAAAAKQWsjsz6dY7vnnnvqF7/4hX7xi19ISlwPyHEcfxovMIzY+uzMM8/U4sWLJUlXX321fv3rX+uQQw7RRRddpMsuuyzP0QEAAAAoCB08YptJV01qJUZsfXfLLbeknp9zzjnq06ePXnzxRfXt21ennXZaHiMDAAAAUDACuiry7oLEtoMdf/zxOv744/MdBgAAAIAC0tpFovy4eNTuhsTWB0888US76zJqCwAAAAD+IrH1wRlnnNGuepZlyXXdjg0GAAAAQOHjUGRfkdj6wPO8fIcAAAAAoBPhUGR/kdh2gMWLF2vx4sXavHlzWtJrWZbmzJmTx8gAAAAAFAwSWN+Q2Pps2rRpuummmzR06FD17t1blmXlOyQAAAAAhYZDkX1FYuuzu+66S/fff78uvPDCfIcCAAAAoEBx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sQUAAACATuz3v/99vkPIO66KDAAAAADo1EhsAR9EIhHdeOONGc97gP/o72DR38Giv4NFfwePPg8W/Y3dBVdFBgAAAAB0aozYAgAAAAA6NRJbAAAAAECnRmILAAAAAOjUSGwBAAAAAJ0aiS0g6c4779RBBx2k4uJiDRkyRCtWrEjNmzp1qvr166du3bppjz320Lhx4/TKK6+02ea6detUWVmpkpIS7bfffrrpppvU/Fpty5Yt05AhQ1RcXKxvfetbuuuuu3zftkLUWn9L0j//+U+ddtppKi8vV48ePXT88cdr48aNrbZJf2fXWn9/+umnuuSSS7TvvvuqtLRUJ510kt55550226S/M1u+fLlOPfVU7bvvvrIsS4899lhqXiwW0y9+8QsdeeSR6tatm/bdd19ddNFF+vjjj9tsl/7OrLX+lqRLLrlElmWlTccff3yb7dLfmbXV39u2bdNVV12lPn36qKSkRIcffrhmzZrVZrv0d2YzZszQMcccox49euib3/ymzjjjDL311ltpdR555BGdeOKJ2muvvWRZltauXduutulzdEkG2M098MADJhwOm3vuucds2LDBXH311aZbt27mo48+MsYY85e//MUsXLjQvPfee2b9+vVm8uTJpqyszGzevDlrm1u2bDH77LOPmTRpklm3bp15+OGHTY8ePczvf//7VJ3333/flJaWmquvvtps2LDB3HPPPSYcDpuHHnqow7c5n9rq73fffdfsueee5mc/+5lZvXq1ee+998w//vEP8+mnn2Ztk/7OrrX+9jzPHH/88WbEiBHm1VdfNW+++ab5t3/7N7P//vubbdu2ZW2T/s7uqaeeMtdff715+OGHjSTz6KOPpuZ9/fXXZty4cWbBggXmzTffNC+99JI57rjjzJAhQ1ptk/7OrrX+NsaYiy++2Jx00knmk08+SU1ffPFFq23S39m11d+XX365Ofjgg82SJUvMBx98YO6++27jOI557LHHsrZJf2d34oknmrlz55r169ebtWvXmpNPPrnF/nnevHlm2rRp5p577jGSzJo1a9pslz5HV0Vii93esccea/7f//t/aWX9+vUzv/zlLzPW37Jli5FkFi1alLXNO++805SXl5u6urpU2YwZM8y+++5rPM8zxhjz85//3PTr1y9tuR/+8Ifm+OOP39lN6RTa6u/zzjvPfP/738+pTfo7u9b6+6233jKSzPr161Pz4vG42XPPPc0999yTtU36u30y/ePf3KuvvmokpX7YyYT+bp9sie3pp5+eUzv0d/tk6u8BAwaYm266Ka1s8ODB5oYbbsjaDv3dfps3bzaSzLJly1rM++CDD9qd2NLn6Ko4FBm7tWg0qlWrVmnChAlp5RMmTNCLL76Ysf7s2bNVXl6uo446KlV+ySWXaNSoUanXL730kiorK9Nuhn7iiSfq448/1ocffpiq03y9J554olauXKlYLObD1hWetvrb8zw9+eSTOvTQQ3XiiSfqm9/8po477riMhxfS321rq7/r6+slScXFxal5juOoqKhIzz//fKqM/u44W7ZskWVZ+sY3vpEqo7/9tXTpUn3zm9/UoYceqh/84AfavHlz2nz62z8nnHCCnnjiCf3rX/+SMUZLlizR22+/rRNPPDFVh/7eeVu2bJEk7bnnnjktR59jd0Fii93a559/Ltd1tc8++6SV77PPPtq0aVPq9T/+8Q91795dxcXFuu2227Rw4ULttddeqfm9e/fW/vvvn3q9adOmjG0m57VWJx6P6/PPP/dnAwtMW/29efNmbdu2TbfccotOOukkPfvsszrzzDN11llnadmyZan69Hf7tNXf/fr10wEHHKDrrrtOX331laLRqG655RZt2rRJn3zySao+/d0x6urq9Mtf/lIXXHCBysrKUuX0t38mTpyov/zlL3ruuef0hz/8QVVVVRozZkzqRx2J/vbTf//3f6t///7q06ePioqKdNJJJ+nOO+/UCSeckKpDf+8cY4yuueYanXDCCTriiCNyWpY+x+4ilO8AgEJgWVbaa2NMWtno0aO1du1aff7557rnnnt07rnn6pVXXtE3v/lNSYkLPLSnzebl7anTFWXrb8/zJEmnn366fvKTn0iSjj76aL344ou66667VFlZKYn+zlW2/g6Hw3r44Yc1efJk7bnnnnIcR+PGjdPEiRPT6tPf/ovFYpo0aZI8z9Odd96ZNo/+9s95552Xen7EEUdo6NChOuCAA/Tkk0/qrLPOkkR/++m///u/9fLLL+uJJ57QAQccoOXLl+uKK65Q7969NW7cOEn098666qqr9Prrr6cdTdNe9Dl2FyS22K3ttddechwnbXRWkjZv3pz2S2W3bt3Ut29f9e3bV8cff7wOOeQQzZkzR9ddd13Gdnv16pWxTanxV9FsdUKhkHr27LnL21aI2urvvfbaS6FQSP3790+bf/jhh7f6ZU5/Z9aez/eQIUO0du1abdmyRdFoVHvvvbeOO+44DR06NGu79PeuicViOvfcc/XBBx/oueeeSxutzYT+9k/v3r11wAEHtHrlb/p759TW1upXv/qVHn30UZ188smSpIEDB2rt2rX6/e9/n0psm6O/2/bjH/9YTzzxhJYvX64+ffrscnv0OboqDkXGbq2oqEhDhgzRwoUL08oXLlyob3/721mXM8akHcrW3LBhw7R8+XJFo9FU2bPPPqt9991XBx54YKpO8/U+++yzGjp0qMLh8E5sTeFrq7+Liop0zDHHtLidwdtvv60DDjgga7v0d2a5fL7Ly8u1995765133tHKlSt1+umnZ22X/t55yaT2nXfe0aJFi9r1DyL97Z8vvvhC1dXV6t27d9Y69PfOicViisVisu30fy0dx0kdjZMJ/Z2dMUZXXXWVHnnkET333HM66KCDfGmXPkeXFeilqoAClLwdypw5c8yGDRvMlClTTLdu3cyHH35otm3bZq677jrz0ksvmQ8//NCsWrXKTJ482UQikbQryf7yl780F154Yer1119/bfbZZx9z/vnnm3Xr1plHHnnElJWVZbyU/k9+8hOzYcMGM2fOnN3iUvqt9bcxxjzyyCMmHA6b2bNnm3feecf88Y9/NI7jmBUrVqTaoL/br63+fvDBB82SJUvMe++9Zx577DFzwAEHmLPOOiutDfq7/bZu3WrWrFlj1qxZYySZmTNnmjVr1piPPvrIxGIxc9ppp5k+ffqYtWvXpt2Cpr6+PtUG/d1+rfX31q1bzU9/+lPz4osvmg8++MAsWbLEDBs2zOy3336mpqYm1Qb93X6t9bcxxlRWVpoBAwaYJUuWmPfff9/MnTvXFBcXmzvvvDPVBv3dfj/60Y9MeXm5Wbp0adr+YseOHak6X3zxhVmzZo158sknjSTzwAMPmDVr1phPPvkkVYc+x+6CxBYwxvzpT38yBxxwgCkqKjKDBw9OXUq/trbWnHnmmWbfffc1RUVFpnfv3ua0004zr776atryF198samsrEwre/31182IESNMJBIxvXr1MlOnTk1dRj9p6dKlZtCgQaaoqMgceOCBZtasWR26nYUiW38nzZkzx/Tt29cUFxebo446qsU9EOnv3LTW3//1X/9l+vTpY8LhsNl///3NDTfckJZkGUN/52LJkiVGUovp4osvTt2OI9O0ZMmSVBv0d/u11t87duwwEyZMMHvvvXfq833xxRebjRs3prVBf7dfa/1tjDGffPKJueSSS8y+++5riouLzWGHHWb+8Ic/pPUd/d1+2fYXc+fOTdWZO3duxjo33nhjqg59jt2FZUzDmeAAAAAAAHRCnGMLAAAAAOjUSGwBAAAAAJ0aiS0AAAAAoFMjsQUAAAAAdGoktgAAAACATo3EFgAAAADQqZHYAgAAAAA6NRJbAAAAAECnRmILAAAAAOjUSGwBAAAAAJ0aiS0AAAAAoFMjsQUAAAAAdGoktgCAgvH666/r0ksv1UEHHaTi4mJ1795dgwcP1m9/+1t9+eWXqXqjRo3SqFGj8hLj1KlTZVlWm/Xmz5+v22+/veMDyuLDDz+UZVn6/e9/71ubS5culWVZWrp0aYfG89RTT2nq1Km5BwgA2G2R2AIACsI999yjIUOGqKqqSj/72c/09NNP69FHH9V3v/td3XXXXZo8eXK+Q8xJvhPbjjB48GC99NJLGjx4cIeu56mnntK0adM6dB0AgK4llO8AAAB46aWX9KMf/Ujjx4/XY489pkgkkpo3fvx4/fSnP9XTTz+dxwghSWVlZTr++OPzHQYAAC0wYgsAyLv//M//lGVZmj17dlpSm1RUVKTTTjut1Ta+/PJLXXHFFdpvv/1UVFSkb33rW7r++utVX1+fqpM8HPb+++9vsbxlWS0Of33yySd19NFHKxKJ6KCDDmr3Yb2jRo3Sk08+qY8++kiWZaWmXGJNxnTVVVfp7rvv1qGHHqpIJKL+/fvrgQceaFccSTNnztRBBx2k7t27a9iwYXr55Zdb1Fm5cqVOO+007bnnniouLtagQYP04IMPptXJdijyPffckxbf/Pnzdckll+jAAw/MOZ5LLrlEf/rTn1Lbn5w+/PDDnLYZALB7YcQWAJBXruvqueee05AhQ1RRUbFTbdTV1Wn06NF67733NG3aNA0cOFArVqzQjBkztHbtWj355JM5t7l48WKdfvrpGjZsmB544AG5rqvf/va3+vTTT9tc9s4779S//du/6b333tOjjz66S7E+8cQTWrJkiW666SZ169ZNd955p84//3yFQiGdc845bcbypz/9Sf369UsdFv3rX/9a3/nOd/TBBx+ovLxckrRkyRKddNJJOu6443TXXXepvLxcDzzwgM477zzt2LFDl1xySdb2Z8+erR/+8Ic6++yzddttt2nLli2aNm1aiyS9vfH8+te/1vbt2/XQQw/ppZdeSi3Xu3fvNrcVALAbMwAA5NGmTZuMJDNp0qR2L1NZWWkqKytTr++66y4jyTz44INp9W699VYjyTz77LPGGGM++OADI8nMnTu3RZuSzI033ph6fdxxx5l9993X1NbWpspqamrMnnvuadrz9XnyySebAw44oEV5e2NNxlRSUmI2bdqUKovH46Zfv36mb9++ra4/ua1HHnmkicfjqfJXX33VSDJ//etfU2X9+vUzgwYNMrFYLK2NU045xfTu3du4rmuMMWbJkiVGklmyZIkxxhjXdU2vXr3Mcccdl7bcRx99ZMLhcNr25xLPlVde2a4+BgAgiUORAQCd3nPPPadu3bq1GMFMjjQuXrw4p/a2b9+uqqoqnXXWWSouLk6V9+jRQ6eeemqgsY4dO1b77LNP6rXjODrvvPP07rvv6v/+7//aXN/JJ58sx3FSrwcOHChJ+uijjyRJ7777rt58801973vfkyTF4/HU9J3vfEeffPKJ3nrrrYxtv/XWW9q0aZPOPffctPL9999fw4cP36l4AADYGSS2AIC82muvvVRaWqoPPvhgp9v44osv1KtXrxa34fnmN7+pUCikL774Iqf2vvrqK3mep169erWYl6msI2NtLYb2bFfPnj3TXifPYa6trZWk1KHV1157rcLhcNp0xRVXSJI+//zzrNsiKS3xTspU1p54AADYGZxjCwDIK8dxNHbsWP3v//6v/u///k99+vTJuY2ePXvqlVdekTEmLWHcvHmz4vG49tprL0lKjb42P/+zeYK4xx57yLIsbdq0qcW6MpV1RKytrS9Z1jxJ3BnJ9V133XU666yzMtY57LDDMpYn15/pvONd7ScAAHLBiC0AIO+uu+46GWP0gx/8QNFotMX8WCymv//971mXHzt2rLZt26bHHnssrXzevHmp+VJiFLG4uFivv/56Wr3HH3887XW3bt107LHH6pFHHlFdXV2qfOvWra3G0VQkEsk4CtneWJMWL16clji6rqsFCxbo4IMP3qkfAZo77LDDdMghh+i1117T0KFDM049evTIumyvXr1aXD1548aNevHFF3c6JkZxAQC5YsQWAJB3w4YN06xZs3TFFVdo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AAAErZURWzFimzMSWwAAAAAIGLf78Zed7wAAAAAAANgVjNgCAAAAQMC4eJS/GLEFAHR5RU9XKXrSMYGsxw3bcscOaXO94WeqFDvxmBavQ4tXp8piJx7Tol6mZVEY4p9sUqh3r3yHAaCzMFbrE3LCiC0AoKDFxw9VaOHKnV4uetIxst3cT1ZKJqfO4lWp18nnmeolhepc2UtXpyW32dpvmqBaDTG6owalv25WT5LsuCepMfFFYXB69FD8k035DgPtEJswVOFnc9+vdEZOt245LxMfN1ShRY39444eImdJy/0fdg3n2PqLEVsAAAAACJppY9pF27Zt0x133KHRo0erZ8+eKi4uVt++fXX55Zerqqrr/ShKYgsAAAAAAUueY5tt2hXPPfecvv3tb2vjxo268cYb9cYbb+jTTz/Vk08+qeHDh+u6667T2Wef7dOWFAYORe5grutq3bp1OuCAA7THHnvkOxwAAAAAhaKDDjnu1auXXnnlFZWUlKSVl5eX67DDDtOll16qV155pWNWnieM2PpsypQpmjNnjqREUltZWanBgweroqJCS5cuzW9wAAAAAApCR47Y9u/fv0VS29xxxx23S+soNIzY+uyhhx7S97//fUnS3//+d33wwQd68803NW/ePF1//fV64YUX8hwhAAAAgLxr7Vxan0ZyL7vsMplWrkQ1d+5cf1ZUAEhsffb555+rV6/Epf6feuopffe739Whhx6qyZMn67//+7/zHB0AAACAwmA1TNnm7bqhQ4dKklavXq21a9fqsssu86XdQkRi67N99tlHGzZsUO/evfX000/rzjvvlCTt2LFDjuPkOToAAAAABSGAEdsrrrhCb775pm6//Xbttdde2nPPPTVp0iR/Gi8wJLY+u/TSS3Xuueeqd+/esixL48ePlyS98sor6tevX56jAwAAAFAQAkhs33//fZ122mmaO3euBg4cqFGjRmm//fbTiBEj/FlBASGx9dnUqVN15JFHauPGjfrud7+rSCQiSXIcR7/85S/zHB0AAACAgmCsxJRtng9OOeUU3XnnnRo+fLgk6bHHHtPEiRO1fv16X9ovJCS2PorFYpowYYLuvvvuFveFuvjii/MUFQAAAIBCY0xiyjbPD7fccovGjRuXel1RUaF58+b503iBqi+JCQAAQTtJREFUIbH1UTgc1vr162VZ/vzCAgAAAKCLCuBQ5KOOOkofffRRWlnPnj39abzAkNj67KKLLtKcOXN0yy235DsUAAAAAIUqgEORjznmGBljZFmW6uvrtXXrVvXs2VOfffaZL+0XEhJbn0WjUd17771auHChhg4dqm7duqXNnzlzZp4iAwAAAFAoLJOYss3zw+bNm9NeP/3003rhhRf8abzAkNj6bP369Ro8eLAk6e23306bxyHKAAAAAPLlpJNO0q9+9StNnz4936H4jsTWZ0uWLMl3CAAAAAAKXQDn2C5btiz13HVdrV69WnV1df40XmBIbDvIu+++q/fee08jR45USUlJ6th2AAAAAAjiHNuf/exnqed1dXWqrq7WokWLfGm70JDY+uyLL77QueeeqyVLlsiyLL3zzjv61re+pcsvv1zf+MY39Ic//CHfIQIAAADItwBGbF999dW01+vWrdMdd9yhu+++258VFBA73wF0NT/5yU8UDoe1ceNGlZaWpsrPO+88Pf3003mMDAAAAEDBMG1MHeDII4/Uyy+/3DGN5xkjtj579tln9cwzz6hPnz5p5YccckiLe0gBAAAA2E0FMGL75z//OfXcdV2tWrVKJSUl/jReYEhsfbZ9+/a0kdqkzz//XJFIJA8RAQAAACg4AZxj++STT6aeh0Ihfetb39Jjjz3mS9uFhsTWZyNHjtS8efNSl9C2LEue5+l3v/udRo8e3a42ZsyYoUceeURvvvmmSkpK9O1vf1u33nqrDjvssI4MHQAAAEBAgriP7YMPPuhPQ50A59j67He/+53uvvtuTZw4UdFoVD//+c91xBFHaPny5br11lvb1cayZct05ZVX6uWXX9bChQsVj8c1YcIEbd++vYOjBwAAABAIn86xXb9+vYYPH67KykqdfPLJ2rZtmxYsWKBhw4ZpzJgxqq6u7oDgCw8jtj7r37+/Xn/9dc2aNUuO42j79u0666yzdOWVV6p3797taqP5Rabmzp2rb37zm1q1apVGjhzZEWEDAAAA6IQOO+wwvfDCC5KkadOm6dFHH9Udd9yhFStWqKqqStOnT9fs2bPzHGXHI7HtAL169dK0adN8a2/Lli2SpD333DNrnfr6etXX16de19TUSJJCJWGFrbBvsSCzcEko7REdi/4OVr772w7bckpy34+llgvbsm0jz7EUzqEdJ5w4qMluWMYJ26nnmeolWY6RVRJusXym5eySsKyGepZtZBxLYTtxXlU4bMt4RraltHqSZFuSF0q8Du1E36CRn59vpzgkO8770ZZ871MkyQrbu83fTrg49/5uvt91ijLv/9CMkVTb/uqWWjkUueEx+T99UiQSaXHdnnC48b3ZsWOH9t9/fw0YMEBFRUUaPny4rr322vYH1YlZxpgOupj07unAAw/UZZddpksvvVQVFRW73J4xRqeffrq++uorrVixImu9qVOnZkym58+fn/FiVgAAAAD8s2PHDl1wwQXasmWLysrKstarqalReXm5DrjlZtnFxRnreHV1+uiX17cov/HGGzV16tQW5QsXLtTPf/5zhcNh3X777XrwwQd1++2364orrtCyZcv0xhtv7PR2dRYMd/jspz/9qe6//37ddNNNGj16tCZPnqwzzzxzp6+IfNVVV+n111/X888/32q96667Ttdcc03qdU1NjSoqKnTf5EcYsQ1AuCSky+acrfsmP6xYbTzf4XR59Hew8t3f7uhBcpas2enlYuMGy3YbRmwXrW738l7l0ZIke9na1Ovk80z1kizPyFrxWovlMy1nL1ur+NjBieXcxIhtkW3pB5f21T1z31XUM7LjXlo9SbLjXuOI7eL2bxNa8vPz7XTvLnfbNp8i67ryvU+RpPiYQQo9l/t+pTMq7lmui/94Uk797Y4aJGdpY/94I4+WvXxtxwTYhcRMLLcF2nG7n+rq6rQkOVtOMX78eK1Zs0a//e1vtWzZstRIb/fu3fXuu+9q0KBBuuyyy/T9739fe+yxR25xdhIktj778Y9/rB//+Md67bXXdN999+nf//3fdcUVV+iCCy7QZZddpsGDB7fdSJO2nnjiCS1fvrzFfXGby3RYgiTFa2ONxzKgw8Vq44rV5rhTw06jv4OVr/6OxzyFdmK9yeWiMS+R2HqWlEM7bsyTJDkNy7gxL/U8U70kyzWya2Mtls+0nFMbU6yhnuUaGc+S1XAocizmpRLbpvWkhsS24Z8ew9+AL/z4fHuhuFzej3bL5z48FvN2m78dpy6RzObS3833u2408/4P6eIdkNiWlZW1OvorJU5JTOYB5eXlikaj2rBhg6LRqE4//XRt2bJFf//73/Xyyy/rxhtv1IknnqjJkydr3LhxucVb4Lgqcgc56qij9F//9V/617/+pRtvvFH33nuvjjnmGB111FG677771NoR4MYYXXXVVXrkkUf0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bLGVK1fakCFDrKysrN4+qXd69dXbzGzSpEk2duxY27dvX+Ln4MGD9fZJvdNrqN4///nP7fzzz7fy8nLbuXOn/fWvf7VwOGwLFy5M2yf1Tm/MmDE2b94827hxo61fv96uueaaOufnv//97/boo4/anDlzTJJVVFQ02C81x5mKYIuz3uWXX25Tp05NmlZaWmoPPvhgyvaHDx82Sfb++++n7XP27NlWUFBgVVVViWlPPPGEFRUVme/7Zmb2m9/8xkpLS5OW+9WvfmVDhw492V0JhIbqPXHiRPvpT3/apD6pd3r11Xvr1q0myTZu3JiY53meFRYW2pw5c9L2Sb0bJ9Ub/9pWr15tkhIf7KRCvRsnXbAdN25ck/qh3o2Tqt79+/e3xx57LGnapZdeag8//HDafqh34x04cMAk2dKlS+vM27lzZ6ODLTXHmYpLkXFWi0QiWrdunUaPHp00ffTo0VqxYkXK9i+88IIKCgp0ySWXJKZPnjxZV111VeLxypUrNWLEiKQvQx8zZoz27t2rXbt2JdrUXu+YMWO0du1aua7bDHt3+mmo3r7v6+2339aFF16oMWPGqEuXLhoyZEjKywupd8Maqvfx48clSXl5eYl54XBYOTk5+vDDDxPTqPepc/jwYTmOo/bt2yemUe/mtWTJEnXp0kUXXnihfvGLX+jAgQNJ86l387nyyiv15ptv6ssvv5SZqby8XJ9++qnGjBmTaEO9T97hw4clSYWFhU1ajprjbEGwxVnt66+/VjQaVdeuXZOmd+3aVfv37088fuutt9S2bVvl5eXpT3/6kxYtWqROnTol5nfv3l3nnntu4vH+/ftT9lk9r742nufp66+/bp4dPM00VO8DBw7o6NGjevLJJzV27Fi99957uv766zVhwgQtXbo00Z56N05D9S4tLVVJSYlmzJihb775RpFIRE8++aT279+vffv2JdpT71OjqqpKDz74oG6++Wbl5+cnplPv5nP11Vfr5Zdf1uLFi/X0009rzZo1+v73v5/4UEei3s1p1qxZ6tevn4qLi5WTk6OxY8dq9uzZuvLKKxNtqPfJMTNNnz5dV155pQYMGNCkZak5zhZZLb0BwOnAcZykx2aWNG3kyJFav369vv76a82ZM0c33HCDVq1apS5dukiK3eChMX3Wnt6YNmeidPX2fV+SNG7cON17772SpIEDB2rFihV6/vnnNWLECEnUu6nS1Ts7O1vz58/XlClTVFhYqHA4rFGjRunqq69Oak+9m5/rurrxxhvl+75mz56dNI96N5+JEycm/j9gwAANHjxYJSUlevvttzVhwgRJ1Ls5zZo1Sx999JHefPNNlZSUaNmyZZo2bZq6d++uUaNGSaLeJ+vOO+/UJ598knQ1TWNRc5wtCLY4q3Xq1EnhcDhpdFaSDhw4kPRJZZs2bdS7d2/17t1bQ4cO1QUXXKC5c+dqxowZKfvt1q1byj6lE5+KpmuTlZWljh07fud9Ox01VO9OnTopKytL/fr1S5rft2/fel/MqXdqjTm+y8rKtH79eh0+fFiRSESdO3fWkCFDNHjw4LT9Uu/vxnVd3XDDDdq5c6cWL16cNFqbCvVuPt27d1dJSUm9d/6m3iensrJSDz30kN544w1dc801kqSLL75Y69ev11NPPZUItrVR74bdddddevPNN7Vs2TIVFxd/5/6oOc5UXIqMs1pOTo7Kysq0aNGipOmLFi3S9773vbTLmVnSpWy1DRs2TMuWLVMkEklMe++991RUVKSePXsm2tRe73vvvafBgwcrOzv7JPbm9NdQvXNycnTZZZfV+TqDTz/9VCUlJWn7pd6pNeX4LigoUOfOnbVt2zatXbtW48aNS9sv9T551aF227Ztev/99xv1BpF6N5+DBw9qz5496t69e9o21PvkuK4r13UVCiW/tQyHw4mrcVKh3umZme68804tWLBAixcvVq9evZqlX2qOM1ZGb1UFnIaqvw5l7ty5tmnTJrvnnnusTZs2tmvXLjt69KjNmDHDVq5cabt27bJ169bZlClTLDc3N+lOsg8++KDdeuuticeHDh2yrl272k033WQbNmywBQsWWH5+fspb6d977722adMmmzt37llxK/366m1mtmDBAsvOzrYXXnjBtm3bZn/5y18sHA7b8uXLE31Q78ZrqN7//Oc/rby83Hbs2GELFy60kpISmzBhQlIf1Lvxvv32W6uoqLCKigqTZM8884xVVFTY559/bq7r2nXXXWfFxcW2fv36pK+gOX78eKIP6t149dX722+/tfvuu89WrFhhO3futPLychs2bJidc845duTIkUQf1Lvx6qu3mdmIESOsf//+Vl5ebp999pnNmzfP8vLybPbs2Yk+qHfj/frXv7aCggJbsmRJ0vni2LFjiTYHDx60iooKe/vtt02Svfrqq1ZRUWH79u1LtKHmOFsQbAEze/bZZ62kpMRycnLs0ksvTdxKv7Ky0q6//norKiqynJwc6969u1133XW2evXqpOUnTZpkI0aMSJr2ySef2PDhwy03N9e6detmM2fOTNxGv9qSJUts0KBBlpOTYz179rTnnnvulO7n6SJdvavNnTvXevfubXl5eXbJJZfU+Q5E6t009dX7z3/+sxUXF1t2drade+659vDDDyeFLDPq3RTl5eUmqc7PpEmTEl/HkeqnvLw80Qf1brz66n3s2DEbPXq0de7cOXF8T5o0yXbv3p3UB/VuvPrqbWa2b98+mzx5shUVFVleXp716dPHnn766aTaUe/GS3e+mDdvXqLNvHnzUrZ55JFHEm2oOc4Wjln8L8EBAAAAAAgg/sYWAAAAABBoBFsAAAAAQKARbAEAAAAAgUawBQAAAAAEGsEWAAAAABBoBFsAAAAAQKARbAEAAAAAgUawBQAgjZkzZ2rgwIEZX++SJUvkOI4cx9H48eMzvv5qPXv2TGzHoUOHWmw7AABoCMEWAHBWqg5s6X4mT56s+++/Xx988EGLbePWrVv10ksvJR5fddVVuueee+q0W7hwoRzHSbSpb7969uwpSdq/f7/uuusunXfeecrNzVWPHj107bXXJu3vmjVrNH/+/FO5iwAANIuslt4AAABawr59+xL/f+211/T73/9eW7duTUxr1aqV2rZtq7Zt27bE5kmSunTpovbt2zdpmQULFigSiUiS9uzZo8svv1zvv/+++vfvL0kKh8PatWuXrrjiCrVv315//OMfdfHFF8t1Xb377ru64447tGXLFklS586dVVhY2Kz7BADAqcCILQDgrNStW7fET0FBgRzHqTOt9qXIkydP1vjx4/X444+ra9euat++vR599FF5nqcHHnhAhYWFKi4u1osvvpi0ri+//FITJ05Uhw4d1LFjR40bN067du06JftVWFiY2IfOnTtLkjp27Jg0bdq0aXIcR6tXr9aPf/xjXXjhherfv7+mT5+ujz766JRsFwAApxLBFgCAJli8eLH27t2rZcuW6ZlnntHMmTP1wx/+UB06dNCqVas0depUTZ06VXv27JEkHTt2TCNHjlTbtm21bNkyffjhh2rbtq3Gjh2bGFnNpP/+97/617/+pTvuuENt2rSpM7+pI8QAAJwOCLYAADRBYWGhZs2apT59+uj2229Xnz59dOzYMT300EO64IILNGPGDOXk5Ojf//63JOnVV19VKBTS3/72N1100UXq27ev5s2bp927d2vJkiUZ3/7t27fLzFRaWprxdQMAcKrwN7YAADRB//79FQqd+Fy4a9euGjBgQOJxOBxWx44ddeDAAUnSunXrtH37drVr1y6pn6qqKu3YsSMzG12DmUlS4mZTAACcCQi2AAA0QXZ2dtJjx3FSTvN9X5Lk+77Kysr08ssv1+mr+m9gGys/P1+HDx+uM/3QoUPKz89vVB8XXHCBHMfR5s2bW/SrhAAAaE5cigwAwCl06aWXatu2berSpYt69+6d9FNQUNCkvkpLS7V27do609esWaM+ffo0qo/CwkKNGTNGzz77rP73v//Vmc/31QIAgohgCwDAKXTLLbeoU6dOGjdunJYvX66dO3dq6dKluvvuu/XFF180qa9p06Zpx44duuOOO/Sf//xHn376qZ599lnNnTtXDzzwQKP7mT17tqLRqC6//HLNnz9f27Zt0+bNmzVr1iwNGzasqbsIAECLI9gCAHAKtW7dWsuWLdO5556rCRMmqG/fvrr99ttVWVnZ6MuHq/Xs2VPLly/Xjh07NHr0aF122WV66aWX9NJLL+knP/lJo/vp1auXPv74Y40cOVL33XefBgwYoB/84Af64IMP9NxzzzV1FwEAaHGOVd9FAgAAnBaWLFmikSNH6ptvvmnxr985nbYFAIB0GLEFAOA0VVxcrJtuuqnF1t+/f39dffXVLbZ+AAAaixFbAABOM5WVlfryyy8lSW3btlW3bt1aZDs+//xzua4rSTrvvPOSvuYIAIDTCcEWAAAAABBofPQKAAAAAAg0gi0AAAAAINAItgAAAACAQCPYAgAAAAACjWALAAAAAAg0gi0AAAAAINAItgAAAACAQCPYAgAAAAACjWALAAAAAAg0gi0AAAAAINAItgAAAACAQCPYAgAAAAACjWALAAAAAAg0gi0AAAAAINAItgAAAACAQCPYAgAAAAACjWALAAAAAAg0gi0AAAAAINAItgAAAACAQCPYAgAAAAACjWALAAAAAAg0gi0AAAAAINAItgAAAACAQCPYAgAAAAACjWALAAAAAAg0gi0AAAAAINAItgAAAACAQCPYAgAAAAACjWALAAAAAAg0gi0AAAAAINAItgAAAACAQCPYAgAAAAACjWALAAAAAAg0gi0AAAAAINAItgAAAACAQCPYAgAAAAACjWALAAAAAAg0gi0AAAAAINAItgAAAACAQCPYAgAAAAACjWALAAAAAAg0gi0AAAAAINAItgAAAACAQCPYAgAAAAACjWALAAAAAAg0gi0AAAAAINAItgAAAACAQCPYAgAAAAACjWALAAAAAAg0gi0AAAAAINAItgAAAACAQCPYAgAAAAACjWALAAAAAAg0gi0AAAAAINAItgAAAACAQCPYAgAAAAACjWALAAAAAAg0gi0AAAAAINAItgAAAACAQCPYAgAAAAACjWALAAAAAAg0gi0AAAAAINAItgAAAACAQCPYAgAAAAACjWALAAAAAAg0gi0AAAAAINAItgAAAACAQCPYAgAAAAACjWALAAAAAAg0gi0AAAAAINAItgAAAACAQCPYAgAAAAACjWALAAAAAAg0gi0AAAAAINAItgAAAACAQCPYAgAAAAACjWALAAAAAAg0gi0AAAAAINAItgAAAACAQCPYAgAAAAACjWALAAAAAAg0gi0AAAAAINAItgAAAACAQCPYAgAAAAACjWALAAAAAAg0gi0AAAAAINAItgAAAACAQCPYAgAAAAAC7f8DnpMhZJ59Xa0AAAAASUVORK5CYII=", - "text/html": [ - "\n", - "
\n", - "
\n", - " Figure\n", - "
\n", - " \n", - "
\n", - " " - ], - "text/plain": [ - "Canvas(toolbar=Toolbar(toolitems=[('Home', 'Reset original view', 'home', 'home'), ('Back', 'Back to previous …" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", - "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", - "\n", - "for i,v in enumerate(variables_to_plot):\n", - " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", - " ts_ax.grid()\n", - "\n", - "plt.show()\n" - ] - }, - { - "cell_type": "markdown", - "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", - "metadata": {}, - "source": [ - "## Field selection dropdown menu\n", - "Select variable to be plotted from a dropdown menu" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", - "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "ec47d67b4aed4584b5a963f8c19161df", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "AppLayout(children=(Dropdown(description='Field:', index=1, layout=Layout(grid_area='header', margin='0px 30% …" - ] - }, - "execution_count": 12, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "plt.ioff()\n", - "\n", - "# populate dropdown menu with available variables \n", - "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", - "d_variable = 'backscatter'\n", - "dropdown = widgets.Dropdown(\n", - " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", - " value= d_variable,\n", - " description='Field:',\n", - " disabled=False,\n", - ")\n", - "dropdown.layout.margin = '0px 30% 0px 20%'\n", - "dropdown.layout.width = '50%'\n", - "\n", - "# set up display\n", - "i_display = act.plotting.TimeSeriesDisplay(ds)\n", - "i_display.add_subplots((1,), figsize = (9.5,5))\n", - "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", - "i_ax.grid()\n", - "i_fig = i_display.fig\n", - "\n", - "# update plot callback function\n", - "def update_plot(change):\n", - " i_ax.cla()\n", - " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", - " i_ax_new.grid()\n", - " i_fig.canvas.draw()\n", - " i_fig.canvas.flush_events()\n", - "\n", - "dropdown.observe(update_plot, names='value')\n", - "\n", - "widgets.AppLayout(\n", - " header=dropdown,\n", - " center=i_fig.canvas,\n", - " pane_heights=[1, 6,1]\n", - ")\n" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.16" - }, - "widgets": { - "application/vnd.jupyter.widget-state+json": { - "state": {}, - "version_major": 2, - "version_minor": 0 - } - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/VAPs/quicklook/OKMSOIL/.ipynb_checkpoints/okmsoil.c1-checkpoint.ipynb b/VAPs/quicklook/OKMSOIL/.ipynb_checkpoints/okmsoil.c1-checkpoint.ipynb deleted file mode 100644 index b2aa8667..00000000 --- a/VAPs/quicklook/OKMSOIL/.ipynb_checkpoints/okmsoil.c1-checkpoint.ipynb +++ /dev/null @@ -1,2048 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "70840257-70e4-45e2-b491-14bff5a257a3", - "metadata": {}, - "source": [ - "# OKMSOIL.C1 Plots\n", - "\n", - "[Click here](https://www.arm.gov/capabilities/vaps/okmsoil) for more information about this vap." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "460fd89f-e034-452c-b837-f65c5958264f", - "metadata": {}, - "outputs": [], - "source": [ - "%matplotlib widget\n", - "import ipywidgets as widgets\n", - "\n", - "import matplotlib.pyplot as plt\n", - "import ipywidgets as widgets\n", - "import numpy as np\n", - "import pandas as pd\n", - "import os\n", - "from datetime import datetime\n", - "\n", - "import act\n", - "import xarray as xr\n", - "\n", - "# Data archive directory\n", - "DATA_DIR = r'/data/archive/'\n", - "\n", - "# Datastream info\n", - "DATASTREAM_NAME = 'okmsoil'\n", - "DATA_LEVEL = 'c1'\n", - "LOCATIONS = [{'end_date': '2020-10-22', 'facility': 'X1', 'site': 'sgp', 'start_date': '1998-01-01'}]" - ] - }, - { - "cell_type": "markdown", - "id": "9faaf875", - "metadata": {}, - "source": [ - "## Define site, facility, and date range" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "ac6764f5", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "The following locations and date ranges are available for this VAP:\n" - ] - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
sitefacilitystart_dateend_date
0sgpX11998-01-012020-10-22
\n", - "
" - ], - "text/plain": [ - " site facility start_date end_date\n", - "0 sgp X1 1998-01-01 2020-10-22" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "print(\"The following locations and date ranges are available for this VAP:\")\n", - "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" - ] - }, - { - "cell_type": "markdown", - "id": "8d132223", - "metadata": {}, - "source": [ - "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "e563983a", - "metadata": {}, - "outputs": [], - "source": [ - "site_facility = ( 'sgp', 'X1' )\n", - "\n", - "date_start = '2020-10-20'\n", - "date_end = '2020-10-22'" - ] - }, - { - "cell_type": "markdown", - "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", - "metadata": {}, - "source": [ - "## Load data files\n", - "Load data files from /data/archive/" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'/data/archive/sgp/sgpokmsoilX1.c1'" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Compile list of files\n", - "site, facility = site_facility\n", - "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", - "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", - "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", - "dir_path\n" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "6be8f3dc", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "['20201020', '20201021', '20201022']" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "from datetime import date, timedelta\n", - "import pandas as pd\n", - "\n", - "def get_ARM_formated_dates(start_date, end_date):\n", - " \"\"\"\n", - " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", - " EXAMPLE:\n", - " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", - " >> [\"20180219\", \"20180220\", \"20180221\"] \n", - " \"\"\"\n", - " \n", - " _start_date = pd.to_datetime(start_date)\n", - " _end_date = pd.to_datetime(end_date)\n", - " \n", - " delta = _end_date - _start_date # returns timedelta \n", - " dates = []\n", - "\n", - " for i in range(delta.days + 1):\n", - " day = _start_date + timedelta(days=i)\n", - " day_formated = day.strftime(format=\"%Y%m%d\")\n", - " dates.append(day_formated)\n", - " return dates\n", - "\n", - "\n", - "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "51feea2e", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "['/data/archive/sgp/sgpokmsoilX1.c1/sgpokmsoilX1.c1.20201020.000000.nc',\n", - " '/data/archive/sgp/sgpokmsoilX1.c1/sgpokmsoilX1.c1.20201021.000000.nc',\n", - " '/data/archive/sgp/sgpokmsoilX1.c1/sgpokmsoilX1.c1.20201022.000000.nc']" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Filter a list of files based on date pattern\n", - "import glob\n", - "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", - "files_filter = []\n", - "for date in dates:\n", - " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", - " files_filter\n", - "files_filter" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "id": "b0e5d0d7", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "3 files loaded\n" - ] - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "
<xarray.Dataset>\n",
-       "Dimensions:                      (time: 144, bound: 2, station_number: 133,\n",
-       "                                  depth: 3)\n",
-       "Coordinates:\n",
-       "  * time                         (time) datetime64[ns] 2020-10-20 ... 2020-10...\n",
-       "  * station_number               (station_number) float32 110.0 1.0 ... 108.0\n",
-       "  * depth                        (depth) int32 5 25 60\n",
-       "Dimensions without coordinates: bound\n",
-       "Data variables: (12/15)\n",
-       "    base_time                    (time) datetime64[ns] 2020-10-20 ... 2020-10-22\n",
-       "    time_offset                  (time) datetime64[ns] 2020-10-20 ... 2020-10...\n",
-       "    time_bounds                  (time, bound) object dask.array<chunksize=(48, 2), meta=np.ndarray>\n",
-       "    sensor_temperature_rise      (time, depth, station_number) float32 dask.array<chunksize=(48, 3, 133), meta=np.ndarray>\n",
-       "    qc_sensor_temperature_rise   (time, depth, station_number) int32 dask.array<chunksize=(48, 3, 133), meta=np.ndarray>\n",
-       "    matric_potential             (time, depth, station_number) float32 dask.array<chunksize=(48, 3, 133), meta=np.ndarray>\n",
-       "    ...                           ...\n",
-       "    fractional_water_index       (time, depth, station_number) float32 dask.array<chunksize=(48, 3, 133), meta=np.ndarray>\n",
-       "    qc_fractional_water_index    (time, depth, station_number) int32 dask.array<chunksize=(48, 3, 133), meta=np.ndarray>\n",
-       "    station                      (time, station_number) |S20 dask.array<chunksize=(48, 133), meta=np.ndarray>\n",
-       "    lat                          (time, station_number) float32 dask.array<chunksize=(48, 133), meta=np.ndarray>\n",
-       "    lon                          (time, station_number) float32 dask.array<chunksize=(48, 133), meta=np.ndarray>\n",
-       "    alt                          (time, station_number) float32 dask.array<chunksize=(48, 133), meta=np.ndarray>\n",
-       "Attributes: (12/53)\n",
-       "    Version:                          $State: xdc-sgp30okm-9.0-1.el5 $\n",
-       "    command_line:                     idl -R -n okmsoil -s sgp -f X1 -b 20201...\n",
-       "    dod_version:                      okmsoil-c1-1.0\n",
-       "    date:                             \n",
-       "    process_version:                  vap-okmsoil-1.0-1.el7\n",
-       "    idl_version:                      \n",
-       "    ...                               ...\n",
-       "    doi:                              10.5439/1432043\n",
-       "    history:                          created by user dsmgr on machine flint ...\n",
-       "    _file_dates:                      ['20201020', '20201021', '20201022']\n",
-       "    _file_times:                      ['000000', '000000', '000000']\n",
-       "    _datastream:                      sgpokmsoilX1.c1\n",
-       "    _arm_standards_flag:              1
" - ], - "text/plain": [ - "\n", - "Dimensions: (time: 144, bound: 2, station_number: 133,\n", - " depth: 3)\n", - "Coordinates:\n", - " * time (time) datetime64[ns] 2020-10-20 ... 2020-10...\n", - " * station_number (station_number) float32 110.0 1.0 ... 108.0\n", - " * depth (depth) int32 5 25 60\n", - "Dimensions without coordinates: bound\n", - "Data variables: (12/15)\n", - " base_time (time) datetime64[ns] 2020-10-20 ... 2020-10-22\n", - " time_offset (time) datetime64[ns] 2020-10-20 ... 2020-10...\n", - " time_bounds (time, bound) object dask.array\n", - " sensor_temperature_rise (time, depth, station_number) float32 dask.array\n", - " qc_sensor_temperature_rise (time, depth, station_number) int32 dask.array\n", - " matric_potential (time, depth, station_number) float32 dask.array\n", - " ... ...\n", - " fractional_water_index (time, depth, station_number) float32 dask.array\n", - " qc_fractional_water_index (time, depth, station_number) int32 dask.array\n", - " station (time, station_number) |S20 dask.array\n", - " lat (time, station_number) float32 dask.array\n", - " lon (time, station_number) float32 dask.array\n", - " alt (time, station_number) float32 dask.array\n", - "Attributes: (12/53)\n", - " Version: $State: xdc-sgp30okm-9.0-1.el5 $\n", - " command_line: idl -R -n okmsoil -s sgp -f X1 -b 20201...\n", - " dod_version: okmsoil-c1-1.0\n", - " date: \n", - " process_version: vap-okmsoil-1.0-1.el7\n", - " idl_version: \n", - " ... ...\n", - " doi: 10.5439/1432043\n", - " history: created by user dsmgr on machine flint ...\n", - " _file_dates: ['20201020', '20201021', '20201022']\n", - " _file_times: ['000000', '000000', '000000']\n", - " _datastream: sgpokmsoilX1.c1\n", - " _arm_standards_flag: 1" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Load files as a single dataset\n", - "files_list = files_filter \n", - "ds = act.io.armfiles.read_netcdf(files_list)\n", - "ds.clean.cleanup()\n", - "print(f'{len(files_list)} files loaded')\n", - "ds\n" - ] - }, - { - "cell_type": "markdown", - "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", - "metadata": {}, - "source": [ - "## Plot time series data\n", - "#### Define the list of variables to be plotted:" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "id": "d09b789e-84f1-4605-846b-a72c110c8048", - "metadata": {}, - "outputs": [], - "source": [ - "variables_to_plot = ['sensor_temperature_rise', 'matric_potential', 'volumetric_water_content']" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", - "metadata": {}, - "outputs": [ - { - "ename": "TypeError", - "evalue": "Dimensions of C (133, 3, 144) should be one smaller than X(144) and Y(3) while using shading='flat' see help(pcolormesh)", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", - "Cell \u001b[0;32mIn[9], line 5\u001b[0m\n\u001b[1;32m 2\u001b[0m ts_display\u001b[38;5;241m.\u001b[39madd_subplots((\u001b[38;5;28mlen\u001b[39m(variables_to_plot),), figsize \u001b[38;5;241m=\u001b[39m (\u001b[38;5;241m9.5\u001b[39m,\u001b[38;5;241m4\u001b[39m\u001b[38;5;241m*\u001b[39m\u001b[38;5;28mlen\u001b[39m(variables_to_plot)))\n\u001b[1;32m 4\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m i,v \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28menumerate\u001b[39m(variables_to_plot):\n\u001b[0;32m----> 5\u001b[0m ts_ax \u001b[38;5;241m=\u001b[39m \u001b[43mts_display\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mplot\u001b[49m\u001b[43m(\u001b[49m\u001b[43mv\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43msubplot_index\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mi\u001b[49m\u001b[43m,\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mset_title\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mds\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mvariables\u001b[49m\u001b[43m[\u001b[49m\u001b[43mv\u001b[49m\u001b[43m]\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mattrs\u001b[49m\u001b[43m[\u001b[49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43mlong_name\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m,\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 6\u001b[0m ts_ax\u001b[38;5;241m.\u001b[39mgrid()\n\u001b[1;32m 8\u001b[0m plt\u001b[38;5;241m.\u001b[39mshow()\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/act/plotting/timeseriesdisplay.py:588\u001b[0m, in \u001b[0;36mTimeSeriesDisplay.plot\u001b[0;34m(self, field, dsname, subplot_index, cmap, set_title, add_nan, day_night_background, invert_y_axis, abs_limits, time_rng, y_rng, use_var_for_y, set_shading, assessment_overplot, overplot_marker, overplot_behind, overplot_markersize, assessment_overplot_category, assessment_overplot_category_color, force_line_plot, labels, cbar_label, cbar_h_adjust, secondary_y, y_axis_flag_meanings, colorbar_labels, cb_friendly, **kwargs)\u001b[0m\n\u001b[1;32m 586\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124medgecolors\u001b[39m\u001b[38;5;124m'\u001b[39m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;129;01min\u001b[39;00m kwargs\u001b[38;5;241m.\u001b[39mkeys():\n\u001b[1;32m 587\u001b[0m kwargs[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124medgecolors\u001b[39m\u001b[38;5;124m'\u001b[39m] \u001b[38;5;241m=\u001b[39m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mface\u001b[39m\u001b[38;5;124m'\u001b[39m\n\u001b[0;32m--> 588\u001b[0m mesh \u001b[38;5;241m=\u001b[39m \u001b[43max\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mpcolormesh\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 589\u001b[0m \u001b[43m \u001b[49m\u001b[43mnp\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43masarray\u001b[49m\u001b[43m(\u001b[49m\u001b[43mxdata\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 590\u001b[0m \u001b[43m \u001b[49m\u001b[43mydata\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 591\u001b[0m \u001b[43m \u001b[49m\u001b[43mdata\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mtranspose\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 592\u001b[0m \u001b[43m \u001b[49m\u001b[43mshading\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mset_shading\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 593\u001b[0m \u001b[43m \u001b[49m\u001b[43mcmap\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcmap\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 594\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 595\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 597\u001b[0m \u001b[38;5;66;03m# Set Title\u001b[39;00m\n\u001b[1;32m 598\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m set_title \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/matplotlib/__init__.py:1442\u001b[0m, in \u001b[0;36m_preprocess_data..inner\u001b[0;34m(ax, data, *args, **kwargs)\u001b[0m\n\u001b[1;32m 1439\u001b[0m \u001b[38;5;129m@functools\u001b[39m\u001b[38;5;241m.\u001b[39mwraps(func)\n\u001b[1;32m 1440\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21minner\u001b[39m(ax, \u001b[38;5;241m*\u001b[39margs, data\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mNone\u001b[39;00m, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs):\n\u001b[1;32m 1441\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m data \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[0;32m-> 1442\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mfunc\u001b[49m\u001b[43m(\u001b[49m\u001b[43max\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;28;43mmap\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43msanitize_sequence\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43margs\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 1444\u001b[0m bound \u001b[38;5;241m=\u001b[39m new_sig\u001b[38;5;241m.\u001b[39mbind(ax, \u001b[38;5;241m*\u001b[39margs, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs)\n\u001b[1;32m 1445\u001b[0m auto_label \u001b[38;5;241m=\u001b[39m (bound\u001b[38;5;241m.\u001b[39marguments\u001b[38;5;241m.\u001b[39mget(label_namer)\n\u001b[1;32m 1446\u001b[0m \u001b[38;5;129;01mor\u001b[39;00m bound\u001b[38;5;241m.\u001b[39mkwargs\u001b[38;5;241m.\u001b[39mget(label_namer))\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/matplotlib/axes/_axes.py:6220\u001b[0m, in \u001b[0;36mAxes.pcolormesh\u001b[0;34m(self, alpha, norm, cmap, vmin, vmax, shading, antialiased, *args, **kwargs)\u001b[0m\n\u001b[1;32m 6217\u001b[0m shading \u001b[38;5;241m=\u001b[39m shading\u001b[38;5;241m.\u001b[39mlower()\n\u001b[1;32m 6218\u001b[0m kwargs\u001b[38;5;241m.\u001b[39msetdefault(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124medgecolors\u001b[39m\u001b[38;5;124m'\u001b[39m, \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mnone\u001b[39m\u001b[38;5;124m'\u001b[39m)\n\u001b[0;32m-> 6220\u001b[0m X, Y, C, shading \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_pcolorargs\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43mpcolormesh\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 6221\u001b[0m \u001b[43m \u001b[49m\u001b[43mshading\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mshading\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mkwargs\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 6222\u001b[0m coords \u001b[38;5;241m=\u001b[39m np\u001b[38;5;241m.\u001b[39mstack([X, Y], axis\u001b[38;5;241m=\u001b[39m\u001b[38;5;241m-\u001b[39m\u001b[38;5;241m1\u001b[39m)\n\u001b[1;32m 6223\u001b[0m \u001b[38;5;66;03m# convert to one dimensional array, except for 3D RGB(A) arrays\u001b[39;00m\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/matplotlib/axes/_axes.py:5751\u001b[0m, in \u001b[0;36mAxes._pcolorargs\u001b[0;34m(self, funcname, shading, *args, **kwargs)\u001b[0m\n\u001b[1;32m 5749\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m shading \u001b[38;5;241m==\u001b[39m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mflat\u001b[39m\u001b[38;5;124m'\u001b[39m:\n\u001b[1;32m 5750\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m (Nx, Ny) \u001b[38;5;241m!=\u001b[39m (ncols \u001b[38;5;241m+\u001b[39m \u001b[38;5;241m1\u001b[39m, nrows \u001b[38;5;241m+\u001b[39m \u001b[38;5;241m1\u001b[39m):\n\u001b[0;32m-> 5751\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mTypeError\u001b[39;00m(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mDimensions of C \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mC\u001b[38;5;241m.\u001b[39mshape\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m should\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 5752\u001b[0m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m be one smaller than X(\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mNx\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m) and Y(\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mNy\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m)\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 5753\u001b[0m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m while using shading=\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mflat\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 5754\u001b[0m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m see help(\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mfuncname\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m)\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[1;32m 5755\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m: \u001b[38;5;66;03m# ['nearest', 'gouraud']:\u001b[39;00m\n\u001b[1;32m 5756\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m (Nx, Ny) \u001b[38;5;241m!=\u001b[39m (ncols, nrows):\n", - "\u001b[0;31mTypeError\u001b[0m: Dimensions of C (133, 3, 144) should be one smaller than X(144) and Y(3) while using shading='flat' see help(pcolormesh)" - ] - }, - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "dc399e8005384c0e9f15675a731f43f7", - "version_major": 2, - "version_minor": 0 - }, - "image/png": "iVBORw0KGgoAAAANSUhEUgAAA7YAAASwCAYAAADPBNYLAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAA9hAAAPYQGoP6dpAABi0UlEQVR4nO3df3RV5Z3o/08gkKA1sUAJIIjRaqXlqkO4IrF8Ha3GQceWGeeKY5eog/c2Vy0DqV5FZvmD5axMO6tO6w9Qr6DjXWgz/hxmbkbNzFhFwRlJg+MIrb1CDWgiTRwT1DYI7O8fLjJNE5Rf5yQPvF5rnT/Ow7OTZ283cb/Z5+QUZFmWBQAAACRqUH8vAAAAAPaHsAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAnbHHvhhRfiggsuiLFjx0ZBQUE89dRTn7nN888/HxUVFVFcXBzHHnts3HPPPblfKAAAQKKEbY59+OGHcfLJJ8ddd921R/M3btwY5513XkyfPj2amprixhtvjLlz58bjjz+e45UCAACkqSDLsqy/F3GoKCgoiCeffDJmzpy52znXX399rFixItavX989Vl1dHa+++mqsXr06D6sEAABIS2F/L4CeVq9eHVVVVT3Gzj333Fi6dGl8/PHHMWTIkD636+rqiq6uru7nO3fujPfeey9GjBgRBQUFOV0zAAAc6rIsi61bt8bYsWNj0CAvjM03YTvAtLa2RllZWY+xsrKy2L59e7S1tcWYMWP63K62tjZuvfXWfCwRAADYjU2bNsW4ceP6exmHHGE7AP32HdZdrxb/tDuvCxYsiJqamu7nHR0dcfTRR8emTZuipKQkNwsFAAAiIqKzszPGjx8fRxxxRH8v5ZAkbAeY0aNHR2tra4+xLVu2RGFhYYwYMWK32xUVFUVRUVGv8ZKSEmELAAB54m2A/cOLvweYadOmRUNDQ4+xZ599NqZMmbLb99cCAAAcyoRtjn3wwQexdu3aWLt2bUR88nE+a9eujebm5oj45CXEs2fP7p5fXV0db731VtTU1MT69etj2bJlsXTp0rj22mv7Y/kAAAADnpci59iaNWvizDPP7H6+632wl112WTz44IPR0tLSHbkREeXl5VFfXx/z58+Pu+++O8aOHRt33HFHXHjhhXlfOwAAQAp8ju1BqrOzM0pLS6Ojo8N7bAEAIMdcf/cvL0UGAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbPNk8eLFUV5eHsXFxVFRURErV6781PnLly+Pk08+OQ477LAYM2ZMXHHFFdHe3p6n1QIAAKRD2OZBXV1dzJs3LxYuXBhNTU0xffr0mDFjRjQ3N/c5/8UXX4zZs2fHnDlz4vXXX49HH300XnnllbjyyivzvHIAAICBT9jmwe233x5z5syJK6+8MiZOnBg/+MEPYvz48bFkyZI+57/88stxzDHHxNy5c6O8vDy++tWvxre+9a1Ys2ZNnlcOAAAw8AnbHNu2bVs0NjZGVVVVj/GqqqpYtWpVn9tUVlbG5s2bo76+PrIsi3fffTcee+yxOP/883f7fbq6uqKzs7PHAwAA4FAgbHOsra0tduzYEWVlZT3Gy8rKorW1tc9tKisrY/ny5TFr1qwYOnRojB49Oo488si48847d/t9amtro7S0tPsxfvz4A7ofAAAAA5WwzZOCgoIez7Ms6zW2y7p162Lu3Llx0003RWNjYzz99NOxcePGqK6u3u3XX7BgQXR0dHQ/Nm3adEDXDwAAMFAV9vcCDnYjR46MwYMH97o7u2XLll53cXepra2N008/Pa677rqIiDjppJPi8MMPj+nTp8dtt90WY8aM6bVNUVFRFBUVHfgdAAAAGODcsc2xoUOHRkVFRTQ0NPQYb2hoiMrKyj63+eijj2LQoJ7/aQYPHhwRn9zpBQAA4D8J2zyoqamJ+++/P5YtWxbr16+P+fPnR3Nzc/dLixcsWBCzZ8/unn/BBRfEE088EUuWLIkNGzbESy+9FHPnzo1TTz01xo4d21+7AQAAMCB5KXIezJo1K9rb22PRokXR0tISkyZNivr6+pgwYUJERLS0tPT4TNvLL788tm7dGnfddVd85zvfiSOPPDLOOuus+O53v9tfuwAAADBgFWRe23pQ6uzsjNLS0ujo6IiSkpL+Xg4AABzUXH/3Ly9FBgAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmzzZPHixVFeXh7FxcVRUVERK1eu/NT5XV1dsXDhwpgwYUIUFRXFcccdF8uWLcvTagEAANJR2N8LOBTU1dXFvHnzYvHixXH66afHvffeGzNmzIh169bF0Ucf3ec2F110Ubz77ruxdOnS+OIXvxhbtmyJ7du353nlAAAAA19BlmVZfy/iYDd16tSYPHlyLFmypHts4sSJMXPmzKitre01/+mnn46LL744NmzYEMOHD9+n79nZ2RmlpaXR0dERJSUl+7x2AADgs7n+7l9eipxj27Zti8bGxqiqquoxXlVVFatWrepzmxUrVsSUKVPie9/7Xhx11FFxwgknxLXXXhu/+tWv8rFkAACApHgpco61tbXFjh07oqysrMd4WVlZtLa29rnNhg0b4sUXX4zi4uJ48skno62tLa666qp47733dvs+266urujq6up+3tnZeeB2AgAAYABzxzZPCgoKejzPsqzX2C47d+6MgoKCWL58eZx66qlx3nnnxe233x4PPvjgbu/a1tbWRmlpafdj/PjxB3wfAAAABiJhm2MjR46MwYMH97o7u2XLll53cXcZM2ZMHHXUUVFaWto9NnHixMiyLDZv3tznNgsWLIiOjo7ux6ZNmw7cTgAAAAxgwjbHhg4dGhUVFdHQ0NBjvKGhISorK/vc5vTTT4933nknPvjgg+6xN954IwYNGhTjxo3rc5uioqIoKSnp8QAAADgUCNs8qKmpifvvvz+WLVsW69evj/nz50dzc3NUV1dHxCd3W2fPnt09/5JLLokRI0bEFVdcEevWrYsXXnghrrvuuviTP/mTGDZsWH/tBgAAwIDkl0fl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pEmTor6+PiZMmBARES0tLT0+0/bee++N7du3x9VXXx1XX3119/hll10WDz74YL6XDwAAMKD5HNuDlM/RAgCA/HH93b+8xxYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwzZPFixdHeXl5FBcXR0VFRaxcufJT5z///PNRUVERxcXFceyxx8Y999yTp5UCAACkRdjmQV1dXcybNy8WLlwYTU1NMX369JgxY0Y0Nzf3OX/jxo1x3nnnxfTp06OpqSluvPHGmDt3bjz++ON5XjkAAMDAV5BlWdbfizjYTZ06NSZPnhxLlizpHps4cWLMnDkzamtre82//vrrY8WKFbF+/fruserq6nj11Vdj9erVe/Q9Ozs7o7S0NDo6OqKkpGT/dwIAANgt19/9yx3bHNu2bVs0NjZGVVVVj/GqqqpYtWpVn9usXr261/xzzz031qxZEx9//HHO1goAAJCiwv5ewMGura0tduzYEWVlZT3Gy8rKorW1tc9tWltb+5y/ffv2aGtrizFjxvTapqurK7q6urqfd3R0RMQn/3IEAADk1q7rbi+I7R/CNk8KCgp6PM+yrNfYZ83va3yX2trauPXWW3uNjx8/fm+XCgAA7KP29vYoLS3t72UccoRtjo0cOTIGDx7c6+7sli1bet2V3WX06NF9zi8sLIwRI0b0uc2CBQuipqam+/n7778fEyZMiObmZn+xPkVnZ2eMHz8+Nm3a5L0Qu+EY7RnH6bM5RnvGcdozjtNnc4z2jOP02RyjPdPR0RFHH310DB8+vL+XckgStjk2dOjQqKioiIaGhviDP/iD7vGGhob4xje+0ec206ZNi7/7u7/rMfbss8/GlClTYsiQIX1uU1RUFEVFRb3GS0tL/QDaAyUlJY7TZ3CM9ozj9Nkcoz3jOO0Zx+mzOUZ7xnH6bI7Rnhk0yK8x6g+Oeh7U1NTE/fffH8uWLYv169fH/Pnzo7m5OaqrqyPik7uts2fP7p5fXV0db731VtTU1MT69etj2bJlsXTp0rj22mv7axcAAAAGLHds82DWrFnR3t4eixYtipaWlpg0aVLU19fHhAkTIiKipaWlx2falpeXR319fcyfPz/uvvvuGDt2bNxxxx1x4YUX9tcuAAAADFjCNk+uuuqquOqqq/r8swcffLDX2BlnnBE/+clP9vn7FRUVxc0339zny5P5T47TZ3OM9ozj9Nkcoz3jOO0Zx+mzOUZ7xnH6bI7RnnGc+ldB5vdRAwAAkDDvsQUAACBpwhYAAICkCVsAAACSJmwHqNra2viv//W/xhFHHBGjRo2KmTNnxs9+9rMec7Isi1tuuSXGjh0bw4YNi9/93d+N119/vcecrq6u+Pa3vx0jR46Mww8/PL7+9a/H5s2be8z5j//4j7j00kujtLQ0SktL49JLL433338/17t4QOTzOP35n/95VFZWxmGHHRZHHnlkrnftgMnXMfrFL34Rc+bMifLy8hg2bFgcd9xxcfPNN8e2bdvysp/7K5/n0te//vU4+uijo7i4OMaMGROXXnppvPPOOznfx/2Vz2P0m3NPOeWUKCgoiLVr1+Zq1w6ofB6nY445JgoKCno8brjhhpzv44GQ7/Pp//7f/xtTp06NYcOGxciRI+MP//APc7p/B0K+jtGPf/zjXufRrscrr7ySl33dH/k8l9544434xje+ESNHjoySkpI4/fTT47nnnsv5Ph4I+TxOP/nJT+Kcc86JI488MkaMGBH/43/8j/jggw9yvo/760Ado/vuuy9+93d/N0pKSqKgoKDP6+qUr78HrIwB6dxzz80eeOCB7N///d+ztWvXZueff3529NFHZx988EH3nL/4i7/IjjjiiOzxxx/PXnvttWzWrFnZmDFjss7Ozu451dXV2VFHHZU1NDRkP/nJT7IzzzwzO/nkk7Pt27d3z/m93/u9bNKkSdmqVauyVatWZZMmTcp+//d/P6/7u6/yeZxuuumm7Pbbb89qamqy0tLSfO7mfsnXMfqHf/iH7PLLL8+eeeaZ7M0338z+9m//Nhs1alT2ne98J+/7vC/yeS7dfvvt2erVq7Nf/OIX2UsvvZRNmzYtmzZtWl73d1/k8xjtMnfu3GzGjBlZRGRNTU352M39ls/jNGHChGzRokVZS0tL92Pr1q153d99lc/j9Nhjj2Wf//znsyVLlmQ/+9nPsp/+9KfZo48+mtf93Rf5OkZdXV09zqGWlpbsyiuvzI455phs586ded/vvZXPc+mLX/xidt5552Wvvvpq9sYbb2RXXXVVdthhh2UtLS153ed9ka/j9Pbbb2ef//zns+rq6uynP/1p9q//+q9ZZWVlduGFF+Z9n/fWgTpGf/VXf5XV1tZmtbW1WURk//Ef/9Hre6V8/T1QCdtEbNmyJYuI7Pnnn8+yLMt27tyZjR49OvuLv/iL7jm//vWvs9LS0uyee+7JsizL3n///WzIkCHZj370o+45b7/9djZo0KDs6aefzrIsy9atW5dFRPbyyy93z1m9enUWEdlPf/rTfOzaAZWr4/SbHnjggaTC9rfl4xjt8r3vfS8rLy/P0Z7kVj6P09/+7d9mBQUF2bZt23K0N7mR62NUX1+fnXjiidnrr7+eVNj+tlwepwkTJmR/9Vd/lZ8dybFcHaePP/44O+qoo7L7778/j3uTG/n6ubRt27Zs1KhR2aJFi3K4N7mTq+P0y1/+MouI7IUXXuie09nZmUVE9o//+I/52LUDKlfH6d57781GjRqV7dixo3tOU1NTFhHZz3/+83zs2gGzL8foNz333HN9hu3Bdv09UHgpciI6OjoiImL48OEREbFx48ZobW2Nqqqq7jlFRUVxxhlnxKpVqyIiorGxMT7++OMec8aOHRuTJk3qnrN69eooLS2NqVOnds857bTTorS0tHtOSnJ1nA4m+TxGHR0d3d8nNfk6Tu+9914sX748KisrY8iQIbnanZzI5TF6991347//9/8e/+f//J847LDD8rE7OZPrc+m73/1ujBgxIk455ZT48z//82Re/v/bcnWcfvKTn8Tbb78dgwYNit/5nd+JMWPGxIwZM3q9dDAF+fq5tGLFimhra4vLL788R3uSW7k6TiNGjIiJEyfGQw89FB9++GFs37497r333igrK4uKiop87d4Bk6vj1NXVFUOHDo1Bg/4zM4YNGxYRES+++GJud+oA25djtCcOtuvvgULYJiDLsqipqYmvfvWrMWnSpIiIaG1tjYiIsrKyHnPLysq6/6y1tTWGDh0an//85z91zqhRo3p9z1GjRnXPSUUuj9PBIp/H6M0334w777wzqqurD/Ru5Fw+jtP1118fhx9+eIwYMSKam5vjb//2b3O1OzmRy2OUZVlcfvnlUV1dHVOmTMn1ruRUrs+lP/3TP40f/ehH8dxzz8U111wTP/jBD+Kqq67K5S7lRC6P04YNGyIi4pZbbok/+7M/i7//+7+Pz3/+83HGGWfEe++9l9P9OpDy+fN76dKlce6558b48eMP9G7kXC6PU0FBQTQ0NERTU1McccQRUVxcHH/1V38VTz/9dFK/eyMit8fprLPOitbW1vjLv/zL2LZtW/zHf/xH3HjjjRER0dLSktP9OpD29RjtiYPp+nsgEbYJuOaaa+Lf/u3f4pFHHun1ZwUFBT2eZ1nWa+y3/facvubvydcZaHJ9nA4G+TpG77zzTvze7/1e/Lf/9t/iyiuv3L9F94N8HKfrrrsumpqa4tlnn43BgwfH7NmzI8uy/V98nuTyGN15553R2dkZCxYsOHAL7ie5Ppfmz58fZ5xxRpx00klx5ZVXxj333BNLly6N9vb2A7MDeZLL47Rz586IiFi4cGFceOGFUVFREQ888EAUFBTEo48+eoD2IPfy9fN78+bN8cwzz8ScOXP2b8H9JJfHKcuyuOqqq2LUqFGxcuXK+Nd//df4xje+Eb//+7+fVLBF5PY4feUrX4m//uu/ju9///tx2GGHxejRo+PYY4+NsrKyGDx48IHbiRw70Mfos77Gvn4d/pOwHeC+/e1vx4oVK+K5556LcePGdY+PHj06IqLXv+ps2bKl+1+RRo8e3f0vZZ8259133+31fX/5y1/2+teogSzXx+lgkK9j9M4778SZZ54Z06ZNi/vuuy8Xu5JT+TpOI0eOjBNOOCHOOeec+NGPfhT19fXx8ssv52KXDrhcH6N//ud/jpdffjmKioqisLAwvvjFL0ZExJQpU+Kyyy7L2X4daP3xc+m0006LiIj/9//+3wHZh3zI9XEaM2ZMRER8+ctf7v7zoqKiOPbYY6O5ufnA71AO5PNceuCBB2LEiBHx9a9//UDvRs7l42fT3//938ePfvSjOP3002Py5MmxePHiGDZsWPz1X/91LnftgMrH+XTJJZdEa2trvP3229He3h633HJL/PKXv4zy8vJc7dYBtT/HaE8cLNffA05O38HLPtu5c2d29dVXZ2PHjs3eeOONPv989OjR2Xe/+93usa6urj7f4F9XV9c955133unzl0f9y7/8S/ecl19+OZk3r+frOP2m1H55VD6P0ebNm7Pjjz8+u/jii/v8DbcDWX+cS7s0NzdnEZE999xzB26HciBfx+itt97KXnvtte7HM888k0VE9thjj2WbNm3K8V7uv/48l/7u7/4ui4jsrbfeOoB7lBv5Ok4dHR1ZUVFRj18eteuXI91777252r0DIt/n0s6dO7Py8vJkfpv9Lvk6TitWrMgGDRrU6zePn3DCCdmf//mf52LXDqj+/Nm0dOnS7LDDDuvztwMPJAfiGP2mz/rlUalefw9UwnaA+p//839mpaWl2Y9//OMev37/o48+6p7zF3/xF1lpaWn2xBNPZK+99lr2x3/8x33+SvZx48Zl//iP/5j95Cc/yc4666w+P+7npJNOylavXp2tXr06+y//5b8k8+vG83mc3nrrraypqSm79dZbs8997nNZU1NT1tTUNOA/WiNfx+jtt9/OvvjFL2ZnnXVWtnnz5h7fKwX5Ok7/8i//kt15551ZU1NT9otf/CL753/+5+yrX/1qdtxxx2W//vWv877feyOff99+08aNG5P6rcj5Ok6rVq3Kbr/99qypqSnbsGFDVldXl40dOzb7+te/nvd93hf5PJ/+9E//NDvqqKOyZ555JvvpT3+azZkzJxs1alT23nvv5XWf91a+/8794z/+YxYR2bp16/K2jwdCvo7TL3/5y2zEiBHZH/7hH2Zr167Nfvazn2XXXnttNmTIkGzt2rV53++9lc/z6c4778waGxuzn/3sZ9ldd92VDRs2LPvhD3+Y1/3dFwfqGLW0tGRNTU3Z//7f/7v7N2k3NTVl7e3t3XNSvv4eqITtABURfT4eeOCB7jk7d+7Mbr755mz06NFZUVFR9v/9f/9f9tprr/X4Or/61a+ya665Jhs+fHg2bNiw7Pd///ez5ubmHnPa29uzb37zm9kRRxyRHXHEEdk3v/nNAf8varvk8zhddtllfX6vgX6XLV/H6IEHHtjt90pBvo7Tv/3bv2VnnnlmNnz48KyoqCg75phjsurq6mzz5s352tV9ls+/b78ptbDN13FqbGzMpk6dmpWWlmbFxcXZl770pezmm2/OPvzww3zt6n7J5/m0bdu27Dvf+U42atSo7IgjjsjOPvvs7N///d/zsZv7Jd9/5/74j/84q6yszPVu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9tlnY8qUKTFkyJA+tykqKoqioqJe46WlpX5Ic0CUlJQ4lzggnEscSM4nDhTnEgfKoEF+jVF/cNTzoKamJu6///5YtmxZrF+/PubPnx/Nzc1RXV0dEZ/cbZ09e3b3/Orq6njrrbeipqYm1q9fH8uWLYulS5fGtdde21+7AAAAMGC5Y5sHs2bNivb29li0aFG0tLTEpEmTor6+PiZMmBARES0tLT0+07a8vDzq6+tj/vz5cffdd8fYsWPjjjvuiAsvvLC/dgEAAGDAErZ5ctVVV8VVV13V5589+OCDvcbOOOOM+MlPfrLP36+oqChuvvnmPl+eDHvDucSB4lziQHI+caA4lzhQnEv9qyDz+6gBAABImPfYAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2CZs8eLFUV5eHsXFxVFRURErV6781PnPP/98VFRURHFxcRx77LFxzz335GmlDHR7cy498cQTcc4558QXvvCFKCkpiWnTpsUzzzyTx9UykO3tz6VdXnrppSgsLIxTTjkltwskGXt7LnV1dcXChQtjwoQJUVRUFMcdd1wsW7YsT6tloNvb82n58uVx8sknx2GHHRZjxoyJK664Itrb2/O0WgaqF154IS644IIYO3ZsFBQUxFNPPfWZ27j+zh9hm6i6urqYN29eLFy4MJqammL69OkxY8aMHp+H+5s2btwY5513XkyfPj2amprixhtvjLlz58bjjz+e55Uz0OztufTCCy/EOeecE/X19dHY2BhnnnlmXHDBBdHU1JTnlTPQ7O25tEtHR0fMnj07vva1r+VppQx0+3IuXXTRRfFP//RPsXTp0vjZz34WjzzySJx44ol5XDUD1d6eTy+++GLMnj075syZE6+//no8+uij8corr8SVV16Z55Uz0Hz44Ydx8sknx1133bVH811/51lGkk499dSsurq6x9iJJ56Y3XDDDX3O/1//639lJ554Yo+xb33rW9lpp52WszWShr09l/ry5S9/Obv11lsP9NJIzL6eS7Nmzcr+7M/+LLv55puzk08+OYcrJBV7ey79wz/8Q1ZaWpq1t7fnY3kkZm/Pp7/8y7/Mjj322B5jd9xxRzZu3LicrZH0RET25JNPfuoc19/55Y5tgrZt2xaNjY1RVVXVY7yqqipWrVrV5zarV6/uNf/cc8+NNWvWxMcff5yztTKw7cu59Nt27twZ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atSo3c6rra2N0tLS7sf48eP3e80AAAApELY5NnLkyBg8eHCvu7NbtmzpdRf3t9XV1cWcOXPib/7mb+Lss8/+1LkLFiyIjo6O7semTZv2e+0AAAApELY5NnTo0KioqIiGhoYe4w0NDVFZWbnb7R555JG4/PLL4+GHH47zzz//M79PUVFRlJSU9HgAAAAcCrzHNg9qamri0ksvjSlTpsS0adPivvvui+bm5qiuro6IT+62vv322/HQQw9FxCdRO3v27PjhD38Yp512Wvfd3mHDhkVpaWm/7QcAAMBAJGzzYNasWdHe3h6LFi2KlpaWmDRpUtTX18eECRMiIqKlpaXHZ9ree++9sX379rj66qvj6quv7h6/7LLL4sEHH8z38gEAAAY0n2N7kPI5WgAAkD+uv/uX99gCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2ObJ4sWLo7y8PIqLi6OioiJWrlz5qfOff/75qKioiOLi4jj22GPjnnvuydNKAQAA0iJs86Curi7mzZsXCxcujKamppg+fXrMmDEjmpub+5y/cePGOO+882L69OnR1NQUN954Y8ydOzcef/zxPK8cAABg4CvIsizr70Uc7KZOnRqTJ0+OJUuWdI9NnDgxZs6cGbW1tb3mX3/99bFixYpYv35991h1dXW8+uqrsXr16j36np2dnVFaWhodHR1RUlKy/zsBAADsluvv/lXY3ws42G3bti0aGxvjhhtu6DFeVVUVq1at6nOb1atXR1VVVY+xc889N5YuXRoff/xxDBkypNc2XV1d0dXV1f28o6MjIj75CwYAAOTWrutu9w37h7DNsba2ttixY0eUlZX1GC8rK4vW1tY+t2ltbe1z/vbt26OtrS3GjBnTa5va2tq49dZbe42PHz9+P1YPAADsjfb29igtLe3vZRxyhG2eFBQU9HieZVmvsc+a39f4LgsWLIiampru5++//35MmDAhmpub/cViv3R2dsb48eNj06ZNXlbDfnEucSA5nzhQnEscKB0dHXH00UfH8OHD+3sphyRhm2MjR46MwYMH97o7u2XLll53ZXcZPXp0n/MLCwtjxIgRfW5TVFQURUVFvcZLS0v9kOaAKCkpcS5xQDiXOJCcTxwoziUOlEGD/H7e/uCo59jQoUOjoqIiGhoaeow3NDREZWVln9tMmzat1/xnn302pkyZ0uf7awEAAA5lwjYPampq4v77749ly5bF+vXrY/78+dHc3BzV1dUR8cnLiGfPnt09v7q6Ot56662oqamJ9evXx7Jly2Lp0qVx7bXX9tcuAAAADFheipwHs2bNivb29li0aFG0tLTEpEmTor6+PiZMmBARES0tLT0+07a8vDzq6+tj/vz5cffdd8fYsWPjjjvuiAsvvHCPv2dRUVHcfPPNfb48GfaGc4kDxbnEgeR84kBxLnGgOJf6l8+xBQAAIGleigwAAEDShC0AAABJE7YAAAAkTdgCAACQNGGbsMWLF0d5eXkUFxdHRUVFrFy58lPnP//881FRURHFxcVx7LHHxj333JOnlTLQ7c259MQTT8Q555wTX/jCF6KkpCSmTZsWzzzzTB5Xy0C2tz+XdnnppZeisLAwTjnllNwukGTs7bnU1dUVCxcujAkTJkRRUVEcd9xxsWzZsjytloFub8+n5cuXx8knnxyHHXZYjBkzJq644opob2/P02oZqF544YW44IILYuzYsVFQUBBPPfXUZ27j+jt/hG2i6urqYt68ebFw4cJoamqK6dOnx4wZM3p8bNBv2rhxY5x33nkxffr0aGpqihtvvDHmzp0bjz/+eJ5XzkCzt+fSCy+8EOecc07U19dHY2NjnHnmmXHBBRdEU1NTnlfOQLO359IuHR0dMXv27Pja176Wp5Uy0O3LuXTRRRfFP/3TP8XSpUvjZz/7WTzyyCNx4okn5nHVDFR7ez69+OKLMXv27JgzZ068/vrr8eijj8Yrr7wSV155ZZ5XzkDz4Ycfxsknnxx33XXXHs13/Z1nGUk69dRTs+rq6h5jJ554YnbDDTf0Of9//a//lZ144ok9xr71rW9lp512Ws7WSBr29lzqy5e//OXs1ltvPdBLIzH7ei7NmjUr+7M/+7Ps5ptvzk4++eQcrpBU7O259A//8A9ZaWlp1t7eno/lkZi9PZ/+8i//Mjv22GN7jN1xxx3ZuHHjcrZG0hMR2ZNPPvmpc1x/55c7tgnatm1bNDY2RlVVVY/xqqqqWLVqVZ/brF69utf8c889N9asWRMff/xxztbKwLYv59Jv27lzZ2zdujWGDx+eiyWSiH09lx544IF488034+abb871EknEvpxLK1asiClTpsT3vve9OOqoo+KEE06Ia6+9Nn71q1/lY8kMYPtyPlVWVsbmzZujvr4+siyLd999Nx577LE4//zz87FkDiKuv/OrsL8XwN5ra2uLHTt2RFlZWY/xsrKyaG1t7XOb1tbWPudv37492traYsyYMTlbLwPXvpxLv+373/9+fPjhh3HRRRflYokkYl/OpZ///Odxww03xMqVK6Ow0P+O+MS+nEsbNmyIF198MYqLi+PJJ5+Mtra2uOqqq+K9997zPttD3L6cT5WVlbF8+fKYNWtW/PrXv47t27fH17/+9bjzzjvzsWQOIq6/88sd24QVFBT0eJ5lWa+xz5rf1ziHnr09l3Z55JFH4pZbbom6uroYNWpUrpZHQvb0XNqxY0dccsklceutt8YJJ5yQr+WRkL35ubRz584oKCiI5cuXx6mnnhrnnXde3H777fHggw+6a0tE7N35tG7dupg7d27cdNNN0djYGE8//XRs3Lgxqqur87FUDjKuv/PHP5EnaOTIkTF48OBe/9K4ZcuWXv8qtMvo0aP7nF9YWBgjRozI2VoZ2PblXNqlrq4u5syZE48++micffbZuVwmCdjbc2nr1q2xZs2aaGpqimuuuSYiPomTLMuisLAwnn322TjrrLPysnYGln35uTRmzJg46qijorS0tHts4sSJkWVZbN68OY4//vicrpmBa1/Op9ra2jj99NPjuuuui4iIk046KQ4//PCYPn163Hbbbe6yscdcf+eXO7YJGjp0aFRUVERDQ0OP8YaGhqisrOxzm2nTpvWa/+yzz8aUKVNiyJAhOVsrA9u+nEsRn9ypvfzyy+Phhx/2niMiYu/PpZKSknjttddi7dq13Y/q6ur40pe+FGvXro2pU6fma+kMMPvyc+n000+Pd955Jz744IPusTfeeCMGDRoU48aNy+l6Gdj25Xz66KOPYtCgnpfIgwcPjoj/vNsGe8L1d5710y+tYj/96Ec/yoYMGZItXbo0W7duXTZv3rzs8MMPz37xi19kWZZlN9xwQ3bppZd2z9+wYUN22GGHZfPnz8/WrVuXLV26NBsyZEj22GOP9dcuMEDs7bn08MMPZ4WFhdndd9+dtbS0dD/ef//9/toFBoi9PZd+m9+KzC57ey5t3bo1GzduXPZHf/RH2euvv549//zz2fHHH59deeWV/bULDCB7ez498MADWWFhYbZ48eLszTffzF588cVsypQp2amnntpfu8AAsXXr1qypqSlramrKIiK7/fbbs6ampuytt97Kssz1d38Ttgm7++67swkTJmRDhw7NJk+enD3//PPdf3bZZZdlZ5xxRo/5P/7xj7Pf+Z3fyYYOHZodc8wx2ZIlS/K8YgaqvTmXzjjjjCwiej0uu+yy/C+cAWdvfy79JmHLb9rbc2n9+vXZ2WefnQ0bNiwbN25cVlNTk3300Ud5XjUD1d6eT3fccUf25S9/ORs2bFg2ZsyY7Jvf/Ga2efPmPK+agea555771Gsg19/9qyDLvKYCAACAdHmPLQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACTt/wcLAF7/XlacegAAAABJRU5ErkJggg==", - "text/html": [ - "\n", - "
\n", - "
\n", - " Figure\n", - "
\n", - " \n", - "
\n", - " " - ], - "text/plain": [ - "Canvas(toolbar=Toolbar(toolitems=[('Home', 'Reset original view', 'home', 'home'), ('Back', 'Back to previous …" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", - "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", - "\n", - "for i,v in enumerate(variables_to_plot):\n", - " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", - " ts_ax.grid()\n", - "\n", - "plt.show()\n" - ] - }, - { - "cell_type": "markdown", - "id": "194399aa-1907-452b-8ba9-bc31d7f60291", - "metadata": {}, - "source": [ - "## Quality check plots\n", - "#### Define variable for QC plot" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", - "metadata": {}, - "outputs": [], - "source": [ - "qc_variable = 'sensor_temperature_rise'" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", - "metadata": {}, - "outputs": [], - "source": [ - "# QC Plot\n", - "if ('qc_'+qc_variable) in ds.variables:\n", - "\n", - " # Plot\n", - " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", - " qc_display.add_subplots((2,), figsize = (9.5,10))\n", - " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", - " qc_ax.grid()\n", - " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", - "\n", - " plt.show()\n", - "else:\n", - " print(f'QC not available for the selected field: {qc_variable}')\n" - ] - }, - { - "cell_type": "markdown", - "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", - "metadata": {}, - "source": [ - "## Field selection dropdown menu\n", - "Select variable to be plotted from a dropdown menu" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", - "metadata": {}, - "outputs": [], - "source": [ - "plt.ioff()\n", - "\n", - "# populate dropdown menu with available variables \n", - "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", - "d_variable = 'sensor_temperature_rise'\n", - "dropdown = widgets.Dropdown(\n", - " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", - " value= d_variable,\n", - " description='Field:',\n", - " disabled=False,\n", - ")\n", - "dropdown.layout.margin = '0px 30% 0px 20%'\n", - "dropdown.layout.width = '50%'\n", - "\n", - "# set up display\n", - "i_display = act.plotting.TimeSeriesDisplay(ds)\n", - "i_display.add_subplots((1,), figsize = (9.5,5))\n", - "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", - "i_ax.grid()\n", - "i_fig = i_display.fig\n", - "\n", - "# update plot callback function\n", - "def update_plot(change):\n", - " i_ax.cla()\n", - " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", - " i_ax_new.grid()\n", - " i_fig.canvas.draw()\n", - " i_fig.canvas.flush_events()\n", - "\n", - "dropdown.observe(update_plot, names='value')\n", - "\n", - "widgets.AppLayout(\n", - " header=dropdown,\n", - " center=i_fig.canvas,\n", - " pane_heights=[1, 6,1]\n", - ")\n" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.16" - }, - "widgets": { - "application/vnd.jupyter.widget-state+json": { - "state": {}, - "version_major": 2, - "version_minor": 0 - } - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/VAPs/quicklook/PBLHT/.ipynb_checkpoints/pblhtsonde1mcfarl.c1-checkpoint.ipynb b/VAPs/quicklook/PBLHT/.ipynb_checkpoints/pblhtsonde1mcfarl.c1-checkpoint.ipynb deleted file mode 100644 index b0b96878..00000000 --- a/VAPs/quicklook/PBLHT/.ipynb_checkpoints/pblhtsonde1mcfarl.c1-checkpoint.ipynb +++ /dev/null @@ -1,679 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "70840257-70e4-45e2-b491-14bff5a257a3", - "metadata": {}, - "source": [ - "# PBLHTSONDE1MCFARL.C1 Plots\n", - "\n", - "[Click here](https://www.arm.gov/capabilities/vaps/pblht) for more information about this vap." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "460fd89f-e034-452c-b837-f65c5958264f", - "metadata": {}, - "outputs": [], - "source": [ - "%matplotlib widget\n", - "import ipywidgets as widgets\n", - "\n", - "import matplotlib.pyplot as plt\n", - "import ipywidgets as widgets\n", - "import numpy as np\n", - "import pandas as pd\n", - "import os\n", - "from datetime import datetime\n", - "\n", - "import act\n", - "import xarray as xr\n", - "\n", - "# Data archive directory\n", - "DATA_DIR = r'/data/archive/'\n", - "\n", - "# Datastream info\n", - "DATASTREAM_NAME = 'pblhtsonde1mcfarl'\n", - "DATA_LEVEL = 'c1'\n", - "LOCATIONS = [{'end_date': '2017-01-02', 'facility': 'M1', 'site': 'awr', 'start_date': '2015-11-30'}, {'end_date': '2016-01-18', 'facility': 'S1', 'site': 'awr', 'start_date': '2015-12-01'}, {'end_date': '2017-10-31', 'facility': 'S1', 'site': 'asi', 'start_date': '2016-04-29'}, {'end_date': '2015-02-09', 'facility': 'M1', 'site': 'acx', 'start_date': '2015-01-12'}, {'end_date': '2020-05-31', 'facility': 'M1', 'site': 'anx', 'start_date': '2019-12-02'}, {'end_date': '2023-12-12', 'facility': 'C1', 'site': 'nsa', 'start_date': '2002-04-28'}, {'end_date': '2012-03-31', 'facility': 'M1', 'site': 'pgh', 'start_date': '2011-06-15'}, {'end_date': '2013-06-29', 'facility': 'M1', 'site': 'pvc', 'start_date': '2012-06-25'}, {'end_date': '2021-06-14', 'facility': 'M1', 'site': 'oli', 'start_date': '2013-10-03'}, {'end_date': '2020-10-01', 'facility': 'M1', 'site': 'mos', 'start_date': '2019-10-11'}, {'end_date': '2015-12-01', 'facility': 'M1', 'site': 'mao', 'start_date': '2014-01-01'}, {'end_date': '2012-04-08', 'facility': 'M1', 'site': 'gan', 'start_date': '2011-09-15'}, {'end_date': '2011-01-05', 'facility': 'M1', 'site': 'grw', 'start_date': '2009-04-16'}, {'end_date': '2013-10-03', 'facility': 'M1', 'site': 'mag', 'start_date': '2012-10-01'}, {'end_date': '2018-03-24', 'facility': 'M1', 'site': 'mar', 'start_date': '2017-10-31'}, {'end_date': '2023-06-15', 'facility': 'M1', 'site': 'guc', 'start_date': '2021-09-01'}, {'end_date': '2023-12-12', 'facility': 'M1', 'site': 'epc', 'start_date': '2023-02-06'}, {'end_date': '2008-12-28', 'facility': 'M1', 'site': 'hfe', 'start_date': '2008-05-14'}, {'end_date': '2019-04-29', 'facility': 'M1', 'site': 'cor', 'start_date': '2018-09-27'}, {'end_date': '2022-10-01', 'facility': 'M1', 'site': 'hou', 'start_date': '2021-09-18'}, {'end_date': '2022-09-25', 'facility': 'S1', 'site': 'hou', 'start_date': '2021-08-28'}, {'end_date': '2023-12-13', 'facility': 'C1', 'site': 'ena', 'start_date': '2013-09-28'}, {'end_date': '2008-01-01', 'facility': 'M1', 'site': 'fkb', 'start_date': '2007-03-24'}, {'end_date': '2007-01-08', 'facility': 'M1', 'site': 'nim', 'start_date': '2006-01-07'}, {'end_date': '2005-09-15', 'facility': 'M1', 'site': 'pye', 'start_date': '2005-02-25'}, {'end_date': '2011-04-24', 'facility': 'M1', 'site': 'sbs', 'start_date': '2010-11-08'}, {'end_date': '2007-06-13', 'facility': 'B1', 'site': 'sgp', 'start_date': '2002-05-13'}, {'end_date': '2007-06-29', 'facility': 'B4', 'site': 'sgp', 'start_date': '2002-05-20'}, {'end_date': '2007-06-22', 'facility': 'B5', 'site': 'sgp', 'start_date': '2002-05-20'}, {'end_date': '2002-11-26', 'facility': 'B6', 'site': 'sgp', 'start_date': '2001-06-20'}, {'end_date': '2023-12-11', 'facility': 'C1', 'site': 'sgp', 'start_date': '2001-04-01'}, {'end_date': '2014-09-12', 'facility': 'M1', 'site': 'tmp', 'start_date': '2014-02-01'}, {'end_date': '2014-07-07', 'facility': 'C1', 'site': 'twp', 'start_date': '2001-04-03'}, {'end_date': '2013-08-25', 'facility': 'C2', 'site': 'twp', 'start_date': '2001-04-01'}, {'end_date': '2015-01-14', 'facility': 'C3', 'site': 'twp', 'start_date': '2002-04-28'}]" - ] - }, - { - "cell_type": "markdown", - "id": "9faaf875", - "metadata": {}, - "source": [ - "## Define site, facility, and date range" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "ac6764f5", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "The following locations and date ranges are available for this VAP:\n" - ] - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
sitefacilitystart_dateend_date
0awrM12015-11-302017-01-02
1awrS12015-12-012016-01-18
2asiS12016-04-292017-10-31
3acxM12015-01-122015-02-09
4anxM12019-12-022020-05-31
5nsaC12002-04-282023-12-12
6pghM12011-06-152012-03-31
7pvcM12012-06-252013-06-29
8oliM12013-10-032021-06-14
9mosM12019-10-112020-10-01
10maoM12014-01-012015-12-01
11ganM12011-09-152012-04-08
12grwM12009-04-162011-01-05
13magM12012-10-012013-10-03
14marM12017-10-312018-03-24
15gucM12021-09-012023-06-15
16epcM12023-02-062023-12-12
17hfeM12008-05-142008-12-28
18corM12018-09-272019-04-29
19houM12021-09-182022-10-01
20houS12021-08-282022-09-25
21enaC12013-09-282023-12-13
22fkbM12007-03-242008-01-01
23nimM12006-01-072007-01-08
24pyeM12005-02-252005-09-15
25sbsM12010-11-082011-04-24
26sgpB12002-05-132007-06-13
27sgpB42002-05-202007-06-29
28sgpB52002-05-202007-06-22
29sgpB62001-06-202002-11-26
30sgpC12001-04-012023-12-11
31tmpM12014-02-012014-09-12
32twpC12001-04-032014-07-07
33twpC22001-04-012013-08-25
34twpC32002-04-282015-01-14
\n", - "
" - ], - "text/plain": [ - " site facility start_date end_date\n", - "0 awr M1 2015-11-30 2017-01-02\n", - "1 awr S1 2015-12-01 2016-01-18\n", - "2 asi S1 2016-04-29 2017-10-31\n", - "3 acx M1 2015-01-12 2015-02-09\n", - "4 anx M1 2019-12-02 2020-05-31\n", - "5 nsa C1 2002-04-28 2023-12-12\n", - "6 pgh M1 2011-06-15 2012-03-31\n", - "7 pvc M1 2012-06-25 2013-06-29\n", - "8 oli M1 2013-10-03 2021-06-14\n", - "9 mos M1 2019-10-11 2020-10-01\n", - "10 mao M1 2014-01-01 2015-12-01\n", - "11 gan M1 2011-09-15 2012-04-08\n", - "12 grw M1 2009-04-16 2011-01-05\n", - "13 mag M1 2012-10-01 2013-10-03\n", - "14 mar M1 2017-10-31 2018-03-24\n", - "15 guc M1 2021-09-01 2023-06-15\n", - "16 epc M1 2023-02-06 2023-12-12\n", - "17 hfe M1 2008-05-14 2008-12-28\n", - "18 cor M1 2018-09-27 2019-04-29\n", - "19 hou M1 2021-09-18 2022-10-01\n", - "20 hou S1 2021-08-28 2022-09-25\n", - "21 ena C1 2013-09-28 2023-12-13\n", - "22 fkb M1 2007-03-24 2008-01-01\n", - "23 nim M1 2006-01-07 2007-01-08\n", - "24 pye M1 2005-02-25 2005-09-15\n", - "25 sbs M1 2010-11-08 2011-04-24\n", - "26 sgp B1 2002-05-13 2007-06-13\n", - "27 sgp B4 2002-05-20 2007-06-29\n", - "28 sgp B5 2002-05-20 2007-06-22\n", - "29 sgp B6 2001-06-20 2002-11-26\n", - "30 sgp C1 2001-04-01 2023-12-11\n", - "31 tmp M1 2014-02-01 2014-09-12\n", - "32 twp C1 2001-04-03 2014-07-07\n", - "33 twp C2 2001-04-01 2013-08-25\n", - "34 twp C3 2002-04-28 2015-01-14" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "print(\"The following locations and date ranges are available for this VAP:\")\n", - "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" - ] - }, - { - "cell_type": "markdown", - "id": "8d132223", - "metadata": {}, - "source": [ - "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "e563983a", - "metadata": {}, - "outputs": [], - "source": [ - "site_facility = ( 'sgp', 'B1' )\n", - "\n", - "date_start = '2007-06-10'\n", - "date_end = '2007-06-12'" - ] - }, - { - "cell_type": "markdown", - "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", - "metadata": {}, - "source": [ - "## Load data files\n", - "Load data files from /data/archive/" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'/data/archive/sgp/sgppblhtsonde1mcfarlB1.c1'" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Compile list of files\n", - "site, facility = site_facility\n", - "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", - "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", - "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", - "dir_path\n" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "6be8f3dc", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "['20070610', '20070611', '20070612']" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "from datetime import date, timedelta\n", - "import pandas as pd\n", - "\n", - "def get_ARM_formated_dates(start_date, end_date):\n", - " \"\"\"\n", - " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", - " EXAMPLE:\n", - " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", - " >> [\"20180219\", \"20180220\", \"20180221\"] \n", - " \"\"\"\n", - " \n", - " _start_date = pd.to_datetime(start_date)\n", - " _end_date = pd.to_datetime(end_date)\n", - " \n", - " delta = _end_date - _start_date # returns timedelta \n", - " dates = []\n", - "\n", - " for i in range(delta.days + 1):\n", - " day = _start_date + timedelta(days=i)\n", - " day_formated = day.strftime(format=\"%Y%m%d\")\n", - " dates.append(day_formated)\n", - " return dates\n", - "\n", - "\n", - "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "51feea2e", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "['/data/archive/sgp/sgppblhtsonde1mcfarlB1.c1/sgppblhtsonde1mcfarlB1.c1.20070611.150200.cdf',\n", - " '/data/archive/sgp/sgppblhtsonde1mcfarlB1.c1/sgppblhtsonde1mcfarlB1.c1.20070611.173000.cdf',\n", - " '/data/archive/sgp/sgppblhtsonde1mcfarlB1.c1/sgppblhtsonde1mcfarlB1.c1.20070611.202900.cdf',\n", - " '/data/archive/sgp/sgppblhtsonde1mcfarlB1.c1/sgppblhtsonde1mcfarlB1.c1.20070611.232800.cdf',\n", - " '/data/archive/sgp/sgppblhtsonde1mcfarlB1.c1/sgppblhtsonde1mcfarlB1.c1.20070612.052900.cdf',\n", - " '/data/archive/sgp/sgppblhtsonde1mcfarlB1.c1/sgppblhtsonde1mcfarlB1.c1.20070612.112900.cdf',\n", - " '/data/archive/sgp/sgppblhtsonde1mcfarlB1.c1/sgppblhtsonde1mcfarlB1.c1.20070612.143100.cdf',\n", - " '/data/archive/sgp/sgppblhtsonde1mcfarlB1.c1/sgppblhtsonde1mcfarlB1.c1.20070612.082900.cdf',\n", - " '/data/archive/sgp/sgppblhtsonde1mcfarlB1.c1/sgppblhtsonde1mcfarlB1.c1.20070612.233900.cdf',\n", - " '/data/archive/sgp/sgppblhtsonde1mcfarlB1.c1/sgppblhtsonde1mcfarlB1.c1.20070612.173100.cdf',\n", - " '/data/archive/sgp/sgppblhtsonde1mcfarlB1.c1/sgppblhtsonde1mcfarlB1.c1.20070612.023100.cdf']" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Filter a list of files based on date pattern\n", - "import glob\n", - "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", - "files_filter = []\n", - "for date in dates:\n", - " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", - " files_filter\n", - "files_filter" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "id": "b0e5d0d7", - "metadata": {}, - "outputs": [ - { - "ename": "ValueError", - "evalue": "Coordinate variable height_ss is neither monotonically increasing nor monotonically decreasing on all datasets", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", - "Cell \u001b[0;32mIn[7], line 3\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[38;5;66;03m# Load files as a single dataset\u001b[39;00m\n\u001b[1;32m 2\u001b[0m files_list \u001b[38;5;241m=\u001b[39m files_filter \n\u001b[0;32m----> 3\u001b[0m ds \u001b[38;5;241m=\u001b[39m \u001b[43mact\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mio\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43marmfiles\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mread_netcdf\u001b[49m\u001b[43m(\u001b[49m\u001b[43mfiles_list\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 4\u001b[0m ds\u001b[38;5;241m.\u001b[39mclean\u001b[38;5;241m.\u001b[39mcleanup()\n\u001b[1;32m 5\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;132;01m{\u001b[39;00m\u001b[38;5;28mlen\u001b[39m(files_list)\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m files loaded\u001b[39m\u001b[38;5;124m'\u001b[39m)\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/act/io/armfiles.py:168\u001b[0m, in \u001b[0;36mread_netcdf\u001b[0;34m(filenames, concat_dim, return_None, combine, decode_times, use_cftime, use_base_time, combine_attrs, cleanup_qc, keep_variables, **kwargs)\u001b[0m\n\u001b[1;32m 164\u001b[0m ds \u001b[38;5;241m=\u001b[39m xr\u001b[38;5;241m.\u001b[39mopen_mfdataset(filenames, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs)\n\u001b[1;32m 166\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m 167\u001b[0m \u001b[38;5;66;03m# When all else fails raise the orginal exception\u001b[39;00m\n\u001b[0;32m--> 168\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m exception\n\u001b[1;32m 170\u001b[0m \u001b[38;5;66;03m# If requested use base_time and time_offset to derive time. Assumes that the units\u001b[39;00m\n\u001b[1;32m 171\u001b[0m \u001b[38;5;66;03m# of both are in seconds and that the value is number of seconds since epoch.\u001b[39;00m\n\u001b[1;32m 172\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m use_base_time:\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/act/io/armfiles.py:143\u001b[0m, in \u001b[0;36mread_netcdf\u001b[0;34m(filenames, concat_dim, return_None, combine, decode_times, use_cftime, use_base_time, combine_attrs, cleanup_qc, keep_variables, **kwargs)\u001b[0m\n\u001b[1;32m 139\u001b[0m except_tuple \u001b[38;5;241m=\u001b[39m except_tuple \u001b[38;5;241m+\u001b[39m (\u001b[38;5;167;01mFileNotFoundError\u001b[39;00m, \u001b[38;5;167;01mOSError\u001b[39;00m)\n\u001b[1;32m 141\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[1;32m 142\u001b[0m \u001b[38;5;66;03m# Read data file with Xarray function\u001b[39;00m\n\u001b[0;32m--> 143\u001b[0m ds \u001b[38;5;241m=\u001b[39m \u001b[43mxr\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mopen_mfdataset\u001b[49m\u001b[43m(\u001b[49m\u001b[43mfilenames\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 145\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m except_tuple \u001b[38;5;28;01mas\u001b[39;00m exception:\n\u001b[1;32m 146\u001b[0m \u001b[38;5;66;03m# If requested return None for File not found error\u001b[39;00m\n\u001b[1;32m 147\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mtype\u001b[39m(exception)\u001b[38;5;241m.\u001b[39m\u001b[38;5;18m__name__\u001b[39m \u001b[38;5;241m==\u001b[39m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mFileNotFoundError\u001b[39m\u001b[38;5;124m'\u001b[39m:\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/backends/api.py:1026\u001b[0m, in \u001b[0;36mopen_mfdataset\u001b[0;34m(paths, chunks, concat_dim, compat, preprocess, engine, data_vars, coords, combine, parallel, join, attrs_file, combine_attrs, **kwargs)\u001b[0m\n\u001b[1;32m 1013\u001b[0m combined \u001b[38;5;241m=\u001b[39m _nested_combine(\n\u001b[1;32m 1014\u001b[0m datasets,\n\u001b[1;32m 1015\u001b[0m concat_dims\u001b[38;5;241m=\u001b[39mconcat_dim,\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 1021\u001b[0m combine_attrs\u001b[38;5;241m=\u001b[39mcombine_attrs,\n\u001b[1;32m 1022\u001b[0m )\n\u001b[1;32m 1023\u001b[0m \u001b[38;5;28;01melif\u001b[39;00m combine \u001b[38;5;241m==\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mby_coords\u001b[39m\u001b[38;5;124m\"\u001b[39m:\n\u001b[1;32m 1024\u001b[0m \u001b[38;5;66;03m# Redo ordering from coordinates, ignoring how they were ordered\u001b[39;00m\n\u001b[1;32m 1025\u001b[0m \u001b[38;5;66;03m# previously\u001b[39;00m\n\u001b[0;32m-> 1026\u001b[0m combined \u001b[38;5;241m=\u001b[39m \u001b[43mcombine_by_coords\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 1027\u001b[0m \u001b[43m \u001b[49m\u001b[43mdatasets\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1028\u001b[0m \u001b[43m \u001b[49m\u001b[43mcompat\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcompat\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1029\u001b[0m \u001b[43m \u001b[49m\u001b[43mdata_vars\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mdata_vars\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1030\u001b[0m \u001b[43m \u001b[49m\u001b[43mcoords\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcoords\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1031\u001b[0m \u001b[43m \u001b[49m\u001b[43mjoin\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mjoin\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1032\u001b[0m \u001b[43m \u001b[49m\u001b[43mcombine_attrs\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcombine_attrs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1033\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 1034\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m 1035\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\n\u001b[1;32m 1036\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;132;01m{}\u001b[39;00m\u001b[38;5;124m is an invalid option for the keyword argument\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 1037\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m ``combine``\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;241m.\u001b[39mformat(combine)\n\u001b[1;32m 1038\u001b[0m )\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/core/combine.py:982\u001b[0m, in \u001b[0;36mcombine_by_coords\u001b[0;34m(data_objects, compat, data_vars, coords, fill_value, join, combine_attrs, datasets)\u001b[0m\n\u001b[1;32m 980\u001b[0m concatenated_grouped_by_data_vars \u001b[38;5;241m=\u001b[39m []\n\u001b[1;32m 981\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m \u001b[38;5;28mvars\u001b[39m, datasets_with_same_vars \u001b[38;5;129;01min\u001b[39;00m grouped_by_vars:\n\u001b[0;32m--> 982\u001b[0m concatenated \u001b[38;5;241m=\u001b[39m \u001b[43m_combine_single_variable_hypercube\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 983\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43mlist\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mdatasets_with_same_vars\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 984\u001b[0m \u001b[43m \u001b[49m\u001b[43mfill_value\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mfill_value\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 985\u001b[0m \u001b[43m \u001b[49m\u001b[43mdata_vars\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mdata_vars\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 986\u001b[0m \u001b[43m \u001b[49m\u001b[43mcoords\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcoords\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 987\u001b[0m \u001b[43m \u001b[49m\u001b[43mcompat\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcompat\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 988\u001b[0m \u001b[43m \u001b[49m\u001b[43mjoin\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mjoin\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 989\u001b[0m \u001b[43m \u001b[49m\u001b[43mcombine_attrs\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcombine_attrs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 990\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 991\u001b[0m concatenated_grouped_by_data_vars\u001b[38;5;241m.\u001b[39mappend(concatenated)\n\u001b[1;32m 993\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m merge(\n\u001b[1;32m 994\u001b[0m concatenated_grouped_by_data_vars,\n\u001b[1;32m 995\u001b[0m compat\u001b[38;5;241m=\u001b[39mcompat,\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 998\u001b[0m combine_attrs\u001b[38;5;241m=\u001b[39mcombine_attrs,\n\u001b[1;32m 999\u001b[0m )\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/core/combine.py:629\u001b[0m, in \u001b[0;36m_combine_single_variable_hypercube\u001b[0;34m(datasets, fill_value, data_vars, coords, compat, join, combine_attrs)\u001b[0m\n\u001b[1;32m 623\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mlen\u001b[39m(datasets) \u001b[38;5;241m==\u001b[39m \u001b[38;5;241m0\u001b[39m:\n\u001b[1;32m 624\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\n\u001b[1;32m 625\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mAt least one Dataset is required to resolve variable names \u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 626\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mfor combined hypercube.\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 627\u001b[0m )\n\u001b[0;32m--> 629\u001b[0m combined_ids, concat_dims \u001b[38;5;241m=\u001b[39m \u001b[43m_infer_concat_order_from_coords\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mlist\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mdatasets\u001b[49m\u001b[43m)\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 631\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m fill_value \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[1;32m 632\u001b[0m \u001b[38;5;66;03m# check that datasets form complete hypercube\u001b[39;00m\n\u001b[1;32m 633\u001b[0m _check_shape_tile_ids(combined_ids)\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/core/combine.py:116\u001b[0m, in \u001b[0;36m_infer_concat_order_from_coords\u001b[0;34m(datasets)\u001b[0m\n\u001b[1;32m 114\u001b[0m ascending \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mFalse\u001b[39;00m\n\u001b[1;32m 115\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[0;32m--> 116\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\n\u001b[1;32m 117\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mCoordinate variable \u001b[39m\u001b[38;5;132;01m{}\u001b[39;00m\u001b[38;5;124m is neither \u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 118\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mmonotonically increasing nor \u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 119\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mmonotonically decreasing on all datasets\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;241m.\u001b[39mformat(dim)\n\u001b[1;32m 120\u001b[0m )\n\u001b[1;32m 122\u001b[0m \u001b[38;5;66;03m# Assume that any two datasets whose coord along dim starts\u001b[39;00m\n\u001b[1;32m 123\u001b[0m \u001b[38;5;66;03m# with the same value have the same coord values throughout.\u001b[39;00m\n\u001b[1;32m 124\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28many\u001b[39m(index\u001b[38;5;241m.\u001b[39msize \u001b[38;5;241m==\u001b[39m \u001b[38;5;241m0\u001b[39m \u001b[38;5;28;01mfor\u001b[39;00m index \u001b[38;5;129;01min\u001b[39;00m indexes):\n", - "\u001b[0;31mValueError\u001b[0m: Coordinate variable height_ss is neither monotonically increasing nor monotonically decreasing on all datasets" - ] - } - ], - "source": [ - "# Load files as a single dataset\n", - "files_list = files_filter \n", - "ds = act.io.armfiles.read_netcdf(files_list)\n", - "ds.clean.cleanup()\n", - "print(f'{len(files_list)} files loaded')\n", - "ds\n" - ] - }, - { - "cell_type": "markdown", - "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", - "metadata": {}, - "source": [ - "## Plot time series data\n", - "#### Define the list of variables to be plotted:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d09b789e-84f1-4605-846b-a72c110c8048", - "metadata": {}, - "outputs": [], - "source": [ - "variables_to_plot = ['pbl_height_heffter', 'pbl_height_liu_liang', 'pbl_height_bulk_richardson_pt25']" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", - "metadata": {}, - "outputs": [], - "source": [ - "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", - "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", - "\n", - "for i,v in enumerate(variables_to_plot):\n", - " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", - " ts_ax.grid()\n", - "\n", - "plt.show()\n" - ] - }, - { - "cell_type": "markdown", - "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", - "metadata": {}, - "source": [ - "## Field selection dropdown menu\n", - "Select variable to be plotted from a dropdown menu" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", - "metadata": {}, - "outputs": [], - "source": [ - "plt.ioff()\n", - "\n", - "# populate dropdown menu with available variables \n", - "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", - "d_variable = 'pbl_height_heffter'\n", - "dropdown = widgets.Dropdown(\n", - " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", - " value= d_variable,\n", - " description='Field:',\n", - " disabled=False,\n", - ")\n", - "dropdown.layout.margin = '0px 30% 0px 20%'\n", - "dropdown.layout.width = '50%'\n", - "\n", - "# set up display\n", - "i_display = act.plotting.TimeSeriesDisplay(ds)\n", - "i_display.add_subplots((1,), figsize = (9.5,5))\n", - "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", - "i_ax.grid()\n", - "i_fig = i_display.fig\n", - "\n", - "# update plot callback function\n", - "def update_plot(change):\n", - " i_ax.cla()\n", - " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", - " i_ax_new.grid()\n", - " i_fig.canvas.draw()\n", - " i_fig.canvas.flush_events()\n", - "\n", - "dropdown.observe(update_plot, names='value')\n", - "\n", - "widgets.AppLayout(\n", - " header=dropdown,\n", - " center=i_fig.canvas,\n", - " pane_heights=[1, 6,1]\n", - ")\n" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.16" - }, - "widgets": { - "application/vnd.jupyter.widget-state+json": { - "state": {}, - "version_major": 2, - "version_minor": 0 - } - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/VAPs/quicklook/PSAP/.ipynb_checkpoints/aospsap3w.c1-checkpoint.ipynb b/VAPs/quicklook/PSAP/.ipynb_checkpoints/aospsap3w.c1-checkpoint.ipynb deleted file mode 100644 index 73feaecb..00000000 --- a/VAPs/quicklook/PSAP/.ipynb_checkpoints/aospsap3w.c1-checkpoint.ipynb +++ /dev/null @@ -1,1841 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "70840257-70e4-45e2-b491-14bff5a257a3", - "metadata": {}, - "source": [ - "# AOSPSAP3W.C1 Plots\n", - "\n", - "[Click here](https://www.arm.gov/capabilities/vaps/psap) for more information about this vap." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "460fd89f-e034-452c-b837-f65c5958264f", - "metadata": {}, - "outputs": [], - "source": [ - "%matplotlib widget\n", - "import ipywidgets as widgets\n", - "\n", - "import matplotlib.pyplot as plt\n", - "import ipywidgets as widgets\n", - "import numpy as np\n", - "import pandas as pd\n", - "import os\n", - "from datetime import datetime\n", - "\n", - "import act\n", - "import xarray as xr\n", - "\n", - "# Data archive directory\n", - "DATA_DIR = r'/data/archive/'\n", - "\n", - "# Datastream info\n", - "DATASTREAM_NAME = 'aospsap3w'\n", - "DATA_LEVEL = 'c1'\n", - "LOCATIONS = [{'end_date': '2013-06-24', 'facility': 'M1', 'site': 'pvc', 'start_date': '2012-07-16'}]" - ] - }, - { - "cell_type": "markdown", - "id": "9faaf875", - "metadata": {}, - "source": [ - "## Define site, facility, and date range" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "ac6764f5", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "The following locations and date ranges are available for this VAP:\n" - ] - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
sitefacilitystart_dateend_date
0pvcM12012-07-162013-06-24
\n", - "
" - ], - "text/plain": [ - " site facility start_date end_date\n", - "0 pvc M1 2012-07-16 2013-06-24" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "print(\"The following locations and date ranges are available for this VAP:\")\n", - "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" - ] - }, - { - "cell_type": "markdown", - "id": "8d132223", - "metadata": {}, - "source": [ - "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "e563983a", - "metadata": {}, - "outputs": [], - "source": [ - "site_facility = ( 'pvc', 'M1' )\n", - "\n", - "date_start = '2013-06-22'\n", - "date_end = '2013-06-24'" - ] - }, - { - "cell_type": "markdown", - "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", - "metadata": {}, - "source": [ - "## Load data files\n", - "Load data files from /data/archive/" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'/data/archive/pvc/pvcaospsap3wM1.c1'" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Compile list of files\n", - "site, facility = site_facility\n", - "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", - "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", - "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", - "dir_path\n" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "6be8f3dc", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "['20130622', '20130623', '20130624']" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "from datetime import date, timedelta\n", - "import pandas as pd\n", - "\n", - "def get_ARM_formated_dates(start_date, end_date):\n", - " \"\"\"\n", - " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", - " EXAMPLE:\n", - " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", - " >> [\"20180219\", \"20180220\", \"20180221\"] \n", - " \"\"\"\n", - " \n", - " _start_date = pd.to_datetime(start_date)\n", - " _end_date = pd.to_datetime(end_date)\n", - " \n", - " delta = _end_date - _start_date # returns timedelta \n", - " dates = []\n", - "\n", - " for i in range(delta.days + 1):\n", - " day = _start_date + timedelta(days=i)\n", - " day_formated = day.strftime(format=\"%Y%m%d\")\n", - " dates.append(day_formated)\n", - " return dates\n", - "\n", - "\n", - "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "51feea2e", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "['/data/archive/pvc/pvcaospsap3wM1.c1/pvcaospsap3wM1.c1.20130622.000000.cdf',\n", - " '/data/archive/pvc/pvcaospsap3wM1.c1/pvcaospsap3wM1.c1.20130623.000000.cdf',\n", - " '/data/archive/pvc/pvcaospsap3wM1.c1/pvcaospsap3wM1.c1.20130624.000000.cdf']" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Filter a list of files based on date pattern\n", - "import glob\n", - "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", - "files_filter = []\n", - "for date in dates:\n", - " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", - " files_filter\n", - "files_filter" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "id": "b0e5d0d7", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "3 files loaded\n" - ] - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "
<xarray.Dataset>\n",
-       "Dimensions:              (time: 3923)\n",
-       "Coordinates:\n",
-       "  * time                 (time) datetime64[ns] 2013-06-22 ... 2013-06-24T18:0...\n",
-       "Data variables: (12/21)\n",
-       "    base_time            (time) datetime64[ns] 2013-06-22 ... 2013-06-24\n",
-       "    time_offset          (time) datetime64[ns] 2013-06-22 ... 2013-06-24T18:0...\n",
-       "    Ba_B_PSAP3W          (time) float32 dask.array<chunksize=(1440,), meta=np.ndarray>\n",
-       "    qc_Ba_B_PSAP3W       (time) int32 dask.array<chunksize=(1440,), meta=np.ndarray>\n",
-       "    Ba_G_PSAP3W          (time) float32 dask.array<chunksize=(1440,), meta=np.ndarray>\n",
-       "    qc_Ba_G_PSAP3W       (time) int32 dask.array<chunksize=(1440,), meta=np.ndarray>\n",
-       "    ...                   ...\n",
-       "    qc_sample_length     (time) int32 dask.array<chunksize=(1440,), meta=np.ndarray>\n",
-       "    impactor_setting     (time) float32 dask.array<chunksize=(1440,), meta=np.ndarray>\n",
-       "    qc_impactor_setting  (time) int32 dask.array<chunksize=(1440,), meta=np.ndarray>\n",
-       "    lat                  (time) float32 42.03 42.03 42.03 ... 42.03 42.03 42.03\n",
-       "    lon                  (time) float32 -70.05 -70.05 -70.05 ... -70.05 -70.05\n",
-       "    alt                  (time) float32 43.0 43.0 43.0 43.0 ... 43.0 43.0 43.0\n",
-       "Attributes: (12/21)\n",
-       "    command_line:             aosmqc_ingest -s pvc -f M1 -n aosmqc -R -D\n",
-       "    process_version:          ingest-aosmqc-1.2-0.el6\n",
-       "    dod_version:              aospsap3w-c1-1.3\n",
-       "    site_id:                  pvc\n",
-       "    facility_id:              M1: Cape Cod, Massachusetts\n",
-       "    data_level:               c1\n",
-       "    ...                       ...\n",
-       "    datastream:               pvcaospsap3wM1.c1\n",
-       "    history:                  created by user dsmgr on machine tin at 2014-06...\n",
-       "    _file_dates:              ['20130622', '20130623', '20130624']\n",
-       "    _file_times:              ['000000', '000000', '000000']\n",
-       "    _datastream:              pvcaospsap3wM1.c1\n",
-       "    _arm_standards_flag:      1
" - ], - "text/plain": [ - "\n", - "Dimensions: (time: 3923)\n", - "Coordinates:\n", - " * time (time) datetime64[ns] 2013-06-22 ... 2013-06-24T18:0...\n", - "Data variables: (12/21)\n", - " base_time (time) datetime64[ns] 2013-06-22 ... 2013-06-24\n", - " time_offset (time) datetime64[ns] 2013-06-22 ... 2013-06-24T18:0...\n", - " Ba_B_PSAP3W (time) float32 dask.array\n", - " qc_Ba_B_PSAP3W (time) int32 dask.array\n", - " Ba_G_PSAP3W (time) float32 dask.array\n", - " qc_Ba_G_PSAP3W (time) int32 dask.array\n", - " ... ...\n", - " qc_sample_length (time) int32 dask.array\n", - " impactor_setting (time) float32 dask.array\n", - " qc_impactor_setting (time) int32 dask.array\n", - " lat (time) float32 42.03 42.03 42.03 ... 42.03 42.03 42.03\n", - " lon (time) float32 -70.05 -70.05 -70.05 ... -70.05 -70.05\n", - " alt (time) float32 43.0 43.0 43.0 43.0 ... 43.0 43.0 43.0\n", - "Attributes: (12/21)\n", - " command_line: aosmqc_ingest -s pvc -f M1 -n aosmqc -R -D\n", - " process_version: ingest-aosmqc-1.2-0.el6\n", - " dod_version: aospsap3w-c1-1.3\n", - " site_id: pvc\n", - " facility_id: M1: Cape Cod, Massachusetts\n", - " data_level: c1\n", - " ... ...\n", - " datastream: pvcaospsap3wM1.c1\n", - " history: created by user dsmgr on machine tin at 2014-06...\n", - " _file_dates: ['20130622', '20130623', '20130624']\n", - " _file_times: ['000000', '000000', '000000']\n", - " _datastream: pvcaospsap3wM1.c1\n", - " _arm_standards_flag: 1" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Load files as a single dataset\n", - "files_list = files_filter \n", - "ds = act.io.armfiles.read_netcdf(files_list)\n", - "ds.clean.cleanup()\n", - "print(f'{len(files_list)} files loaded')\n", - "ds\n" - ] - }, - { - "cell_type": "markdown", - "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", - "metadata": {}, - "source": [ - "## Plot time series data\n", - "#### Define the list of variables to be plotted:" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "id": "d09b789e-84f1-4605-846b-a72c110c8048", - "metadata": {}, - "outputs": [], - "source": [ - "variables_to_plot = ['transmittance_blue', 'dqrvar_transmittance_blue', 'transmittance_green']" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", - "metadata": {}, - "outputs": [ - { - "ename": "KeyError", - "evalue": "'transmittance_blue'", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mKeyError\u001b[0m Traceback (most recent call last)", - "Cell \u001b[0;32mIn[9], line 5\u001b[0m\n\u001b[1;32m 2\u001b[0m ts_display\u001b[38;5;241m.\u001b[39madd_subplots((\u001b[38;5;28mlen\u001b[39m(variables_to_plot),), figsize \u001b[38;5;241m=\u001b[39m (\u001b[38;5;241m9.5\u001b[39m,\u001b[38;5;241m4\u001b[39m\u001b[38;5;241m*\u001b[39m\u001b[38;5;28mlen\u001b[39m(variables_to_plot)))\n\u001b[1;32m 4\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m i,v \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28menumerate\u001b[39m(variables_to_plot):\n\u001b[0;32m----> 5\u001b[0m ts_ax \u001b[38;5;241m=\u001b[39m ts_display\u001b[38;5;241m.\u001b[39mplot(v, subplot_index\u001b[38;5;241m=\u001b[39m(i,), set_title\u001b[38;5;241m=\u001b[39m\u001b[43mds\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mvariables\u001b[49m\u001b[43m[\u001b[49m\u001b[43mv\u001b[49m\u001b[43m]\u001b[49m\u001b[38;5;241m.\u001b[39mattrs[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mlong_name\u001b[39m\u001b[38;5;124m'\u001b[39m],)\n\u001b[1;32m 6\u001b[0m ts_ax\u001b[38;5;241m.\u001b[39mgrid()\n\u001b[1;32m 8\u001b[0m plt\u001b[38;5;241m.\u001b[39mshow()\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/core/utils.py:453\u001b[0m, in \u001b[0;36mFrozen.__getitem__\u001b[0;34m(self, key)\u001b[0m\n\u001b[1;32m 452\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21m__getitem__\u001b[39m(\u001b[38;5;28mself\u001b[39m, key: K) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m V:\n\u001b[0;32m--> 453\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mmapping\u001b[49m\u001b[43m[\u001b[49m\u001b[43mkey\u001b[49m\u001b[43m]\u001b[49m\n", - "\u001b[0;31mKeyError\u001b[0m: 'transmittance_blue'" - ] - }, - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "f164bc0d8b484beaa248597fc1245960", - "version_major": 2, - "version_minor": 0 - }, - "image/png": "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1tnZmUVE9thjj2WdnZ3ZW2+9lWWZ6+/JJmwT9uSTT2aVlZVZYWFhNn/+/Gznzp3D/+zuu+/ObrrpphHH/+d//mf2J3/yJ1lhYWH2uc99Ltu8eXOOJ2aqGs9euummm7KIGPW4++67cz84U854f1/6XcKW3zXevXTw4MHs1ltvzS677LJs9uzZWXNzc/bBBx/keGqmqvHup8cffzz74he/mF122WVZeXl59ld/9VfZ0aNHczw1U81//Md/fOw1kOvvyZWXZd5TAQAAQLp8xhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAnbCbZr165YtmxZzJo1K/Ly8uKVV175xHN27twZ1dXVUVxcHNddd1089dRTEz8oAABAooTtBHv//fdj3rx58cQTT5zT8YcPH46lS5dGXV1ddHZ2xkMPPRSrVq2KF198cYInBQAASFNelmXZZA9xqcjLy4uXX345li9fftZjvv3tb8err74aBw8eHF5ramqKn/3sZ7Fv374cTAkAAJCWgskegJH27dsX9fX1I9Zuv/322LJlS3z44Ycxffr0Mc8bGhqKoaGh4eenT5+Od999N2bMmBF5eXkTOjMAAFzqsiyL48ePx6xZs2LaNG+MzTVhO8X09vZGWVnZiLWysrI4efJk9PX1RXl5+ZjntbS0xIYNG3IxIgAAcBZHjhyJ2bNnT/YYlxxhOwX9/h3WM+8W/7g7r+vWrYvm5ubh5wMDA3HttdfGkSNHoqSkZGIGBQAAIiJicHAwKioq4g//8A8ne5RLkrCdYq6++uro7e0dsXbs2LEoKCiIGTNmnPW8oqKiKCoqGrVeUlIibAEAIEd8DHByePP3FLNo0aJob28fsbZjx46oqak56+drAQAALmXCdoK999570dXVFV1dXRHx0df5dHV1RXd3d0R89BbixsbG4eObmprirbfeiubm5jh48GBs3bo1tmzZEg888MBkjA8AADDleSvyBNu/f3/cfPPNw8/P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7dixY1FQUBAzZsw463lFRUVRVFQ0ar2kpETYAgBAjvgY4OTw5u8pZtGiRdHe3j5ibceOHVFTU3PWz9cCAABcyoTtBHvvvfeiq6srurq6IuKjr/Pp6uqK7u7uiPjoLcSNjY3Dxzc1NcVbb70Vzc3NcfDgwdi6dWts2bIlHnjggckYHwAAYMrzVuQJtn///rj55puHn5/5HOzdd98d27Zti56enuHIjYioqqqKtra2WLNmTTz55JMxa9asePzxx+NrX/tazmcHAABIge+xvUgNDg5GaWlpDAwM+IwtAABMMNffk8tbkQEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAnbHNm0aVNUVVVFcXFxVFdXx+7duz/2+O3bt8e8efPi8ssvj/Ly8rj33nujv78/R9MCAACkQ9jmQGtra6xevTrWr18fnZ2dUVdXF0uWLInu7u4xj9+zZ080NjbGypUr44033ojnn38+fvrTn8Z9992X48kBAACmPmGbA4899lisXLky7rvvvpgzZ0780z/9U1RUVMTmzZvHPP6//uu/4nOf+1ysWrUqqqqq4k//9E/jG9/4Ruzfvz/HkwMAAEx9wnaCnThxIjo6OqK+vn7Een19fezdu3fMc2pra+Po0aPR1tYWWZbFO++8Ey+88ELccccduRgZAAAgKcJ2gvX19cWpU6eirKxsxHpZWVn09vaOeU5tbW1s3749GhoaorCwMK6++ur4zGc+E9///vfP+nOGhoZicHBwxAMAAOBSIGxzJC8vb8TzLMtGrZ1x4MCBWLVqVTz88MPR0dERr732Whw+fDiamprO+uu3tLREaWnp8KOiouKCzg8AADBV5WVZlk32EBezEydOxOWXXx7PP/98/MVf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yZlq3n///Zg3b1488cQT53S86+8cy0jSggULsqamphFrN954Y7Z27doxj/+7v/u77MYbbxyx9o1vfCP7yle+MmEzkobx7qWxfPGLX8w2bNhwoUcjMee7lxoaGrK///u/zx555JFs3rx5EzghqRjvXvq3f/u3rLS0NOvv78/FeCRmvPvpH//xH7PrrrtuxNrjjz+ezZ49e8JmJD0Rkb388ssfe4zr79xyxzZBJ06ciI6Ojqivrx+xXl9fH3v37h3znH379o06/vbbb4/9+/fHhx9+OGGzMrWdz176fadPn47jx4/HlVdeOREjkojz3UvPPPNMvPnmm/HII49M9Igk4nz20quvvho1NTXx3e9+N6655pq44YYb4oEHHojf/OY3uRiZKex89lNtbW0cPXo02traIsuyeOedd+KFF16IO+64IxcjcxFx/Z1bBZM9AOPX19cXp06dirKyshHrZWVl0dvbO+Y5vb29Yx5/8uTJ6Ovri/Ly8gmbl6nrfPbS7/ve974X77//fqxYsWIiRiQR57OXfvnLX8batWtj9+7dUVDgP0d85Hz20qFDh2LPnj1RXFwcL7/8cvT19cX9998f7777rs/ZXuLOZz/V1tbG9u3bo6GhIX7729/GyZMn48/+7M/i+9//fi5G5iLi+ju33LFNWF5e3ojnWZaNWvuk48da59Iz3r10xnPPPRff+c53orW1Na666qqJGo+EnOteOnXqVNx5552xYcOGuOGGG3I1HgkZz+9Lp0+fjry8vNi+fXssWLAgli5dGo899lhs27bNXVsiYnz76cCBA7Fq1ap4+OGHo6OjI1577bU4fPhwNDU15WJULjKuv3PHH5EnaObMmZGfnz/qTxqPHTs26k+Fzrj66qvHPL6goCBmzJgxYbMytZ3PXjqjtbU1Vq5cGc8//3zceuutEzkmCRjvXjp+/Hjs378/Ojs741vf+lZEfBQnWZZFQUFB7NixI2655ZaczM7Ucj6/L5WXl8c111wTpaWlw2tz5syJLMvi6NGjcf3110/ozExd57OfWlpaYvHixfHggw9GRMSXvvSluOKKK6Kuri4effRRd9k4Z66/c8sd2wQVFhZGdXV1tLe3j1hvb2+P2traMc9ZtGjRqON37NgRNTU1MX369AmblantfPZSxEd3au+555549tlnfeaIiBj/XiopKYmf//zn0dXVNfxoamqKL3zhC9HV1RULFy7M1ehMMefz+9LixYvj7bffjvfee2947Re/+EVMmzYtZs+ePaHzMrWdz3764IMPYtq0kZfI+fn5EfF/d9vgXLj+zrFJ+kur+JR+/OMfZ9OnT8+2bNmSHThwIFu9enV2xRVXZP/7v/+bZVmWrV27NrvrrruGjz906FB2+eWXZ2vWrMkOHDiQbdmyJZs+fXr2wgsvTNZLYIoY71569tlns4KCguzJJ5/Menp6hh+//vWvJ+slMEWMdy/9Pn8rMmeMdy8dP348mz17dvaXf/mX2RtvvJHt3Lkzu/7667P77rtvsl4CU8h499MzzzyTFRQUZJs2bcrefPPNbM+ePVlNTU22YMGCyXoJTBHHjx/POjs7s87Oziwissceeyzr7OzM3nrrrSzLXH9PNmGbsCeffDKrrKzMCgsLs/nz52c7d+4c/md33313dtNNN404/j//8z+zP/mTP8kKCwuzz33uc9nmzZtzPDFT1Xj20k033ZRFxKjH3XffnfvBmXLG+/vS7xK2/K7x7qWDBw9mt956a3bZZZdls2fPzpqbm7MPPvggx1MzVY13Pz3++OPZF7/4xeyyyy7LysvLs7/6q7/Kjh49muOpmWr+4z/+42OvgVx/T668LPOeCgAAANLlM7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQtP8PDcrXAZbhxkUAAAAASUVORK5CYII=", - "text/html": [ - "\n", - "
\n", - "
\n", - " Figure\n", - "
\n", - " \n", - "
\n", - " " - ], - "text/plain": [ - "Canvas(toolbar=Toolbar(toolitems=[('Home', 'Reset original view', 'home', 'home'), ('Back', 'Back to previous …" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", - "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", - "\n", - "for i,v in enumerate(variables_to_plot):\n", - " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", - " ts_ax.grid()\n", - "\n", - "plt.show()\n" - ] - }, - { - "cell_type": "markdown", - "id": "194399aa-1907-452b-8ba9-bc31d7f60291", - "metadata": {}, - "source": [ - "## Quality check plots\n", - "#### Define variable for QC plot" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", - "metadata": {}, - "outputs": [], - "source": [ - "qc_variable = 'transmittance_blue'" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", - "metadata": {}, - "outputs": [], - "source": [ - "# QC Plot\n", - "if ('qc_'+qc_variable) in ds.variables:\n", - "\n", - " # Plot\n", - " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", - " qc_display.add_subplots((2,), figsize = (9.5,10))\n", - " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", - " qc_ax.grid()\n", - " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", - "\n", - " plt.show()\n", - "else:\n", - " print(f'QC not available for the selected field: {qc_variable}')\n" - ] - }, - { - "cell_type": "markdown", - "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", - "metadata": {}, - "source": [ - "## Field selection dropdown menu\n", - "Select variable to be plotted from a dropdown menu" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", - "metadata": {}, - "outputs": [], - "source": [ - "plt.ioff()\n", - "\n", - "# populate dropdown menu with available variables \n", - "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", - "d_variable = 'transmittance_blue'\n", - "dropdown = widgets.Dropdown(\n", - " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", - " value= d_variable,\n", - " description='Field:',\n", - " disabled=False,\n", - ")\n", - "dropdown.layout.margin = '0px 30% 0px 20%'\n", - "dropdown.layout.width = '50%'\n", - "\n", - "# set up display\n", - "i_display = act.plotting.TimeSeriesDisplay(ds)\n", - "i_display.add_subplots((1,), figsize = (9.5,5))\n", - "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", - "i_ax.grid()\n", - "i_fig = i_display.fig\n", - "\n", - "# update plot callback function\n", - "def update_plot(change):\n", - " i_ax.cla()\n", - " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", - " i_ax_new.grid()\n", - " i_fig.canvas.draw()\n", - " i_fig.canvas.flush_events()\n", - "\n", - "dropdown.observe(update_plot, names='value')\n", - "\n", - "widgets.AppLayout(\n", - " header=dropdown,\n", - " center=i_fig.canvas,\n", - " pane_heights=[1, 6,1]\n", - ")\n" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.16" - }, - "widgets": { - "application/vnd.jupyter.widget-state+json": { - "state": {}, - "version_major": 2, - "version_minor": 0 - } - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/VAPs/quicklook/RADFLUXANAL/.ipynb_checkpoints/radflux1long.c1-checkpoint.ipynb b/VAPs/quicklook/RADFLUXANAL/.ipynb_checkpoints/radflux1long.c1-checkpoint.ipynb deleted file mode 100644 index 771298d9..00000000 --- a/VAPs/quicklook/RADFLUXANAL/.ipynb_checkpoints/radflux1long.c1-checkpoint.ipynb +++ /dev/null @@ -1,3763 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "70840257-70e4-45e2-b491-14bff5a257a3", - "metadata": {}, - "source": [ - "# RADFLUX1LONG.C1 Plots\n", - "\n", - "[Click here](https://www.arm.gov/capabilities/vaps/radfluxanal) for more information about this vap." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "460fd89f-e034-452c-b837-f65c5958264f", - "metadata": {}, - "outputs": [], - "source": [ - "%matplotlib widget\n", - "import ipywidgets as widgets\n", - "\n", - "import matplotlib.pyplot as plt\n", - "import ipywidgets as widgets\n", - "import numpy as np\n", - "import pandas as pd\n", - "import os\n", - "from datetime import datetime\n", - "\n", - "import act\n", - "import xarray as xr\n", - "\n", - "# Data archive directory\n", - "DATA_DIR = r'/data/archive/'\n", - "\n", - "# Datastream info\n", - "DATASTREAM_NAME = 'radflux1long'\n", - "DATA_LEVEL = 'c1'\n", - "LOCATIONS = [{'end_date': '2023-09-14', 'facility': 'C1', 'site': 'nsa', 'start_date': '2021-07-04'}, {'end_date': '2021-06-12', 'facility': 'M1', 'site': 'oli', 'start_date': '2020-10-16'}, {'end_date': '2023-09-28', 'facility': 'E11', 'site': 'sgp', 'start_date': '2020-06-08'}, {'end_date': '2023-10-30', 'facility': 'E12', 'site': 'sgp', 'start_date': '2020-06-07'}, {'end_date': '2023-10-26', 'facility': 'E13', 'site': 'sgp', 'start_date': '2020-06-03'}, {'end_date': '2023-09-28', 'facility': 'E15', 'site': 'sgp', 'start_date': '2021-07-12'}, {'end_date': '2023-09-28', 'facility': 'E9', 'site': 'sgp', 'start_date': '2021-07-04'}, {'end_date': '2021-09-20', 'facility': 'E31', 'site': 'sgp', 'start_date': '2020-06-08'}, {'end_date': '2023-10-31', 'facility': 'E32', 'site': 'sgp', 'start_date': '2021-07-03'}, {'end_date': '2023-10-31', 'facility': 'E33', 'site': 'sgp', 'start_date': '2020-06-11'}, {'end_date': '2023-09-28', 'facility': 'E34', 'site': 'sgp', 'start_date': '2020-06-18'}, {'end_date': '2023-09-28', 'facility': 'E35', 'site': 'sgp', 'start_date': '2019-06-24'}, {'end_date': '2023-09-28', 'facility': 'E36', 'site': 'sgp', 'start_date': '2020-06-26'}, {'end_date': '2023-10-31', 'facility': 'E37', 'site': 'sgp', 'start_date': '2020-07-01'}, {'end_date': '2021-05-29', 'facility': 'E38', 'site': 'sgp', 'start_date': '2020-06-25'}, {'end_date': '2023-10-30', 'facility': 'E39', 'site': 'sgp', 'start_date': '2020-09-07'}, {'end_date': '2023-09-28', 'facility': 'E40', 'site': 'sgp', 'start_date': '2020-09-07'}, {'end_date': '2023-08-02', 'facility': 'E41', 'site': 'sgp', 'start_date': '2021-07-31'}]" - ] - }, - { - "cell_type": "markdown", - "id": "9faaf875", - "metadata": {}, - "source": [ - "## Define site, facility, and date range" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "ac6764f5", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "The following locations and date ranges are available for this VAP:\n" - ] - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
sitefacilitystart_dateend_date
0nsaC12021-07-042023-09-14
1oliM12020-10-162021-06-12
2sgpE112020-06-082023-09-28
3sgpE122020-06-072023-10-30
4sgpE132020-06-032023-10-26
5sgpE152021-07-122023-09-28
6sgpE92021-07-042023-09-28
7sgpE312020-06-082021-09-20
8sgpE322021-07-032023-10-31
9sgpE332020-06-112023-10-31
10sgpE342020-06-182023-09-28
11sgpE352019-06-242023-09-28
12sgpE362020-06-262023-09-28
13sgpE372020-07-012023-10-31
14sgpE382020-06-252021-05-29
15sgpE392020-09-072023-10-30
16sgpE402020-09-072023-09-28
17sgpE412021-07-312023-08-02
\n", - "
" - ], - "text/plain": [ - " site facility start_date end_date\n", - "0 nsa C1 2021-07-04 2023-09-14\n", - "1 oli M1 2020-10-16 2021-06-12\n", - "2 sgp E11 2020-06-08 2023-09-28\n", - "3 sgp E12 2020-06-07 2023-10-30\n", - "4 sgp E13 2020-06-03 2023-10-26\n", - "5 sgp E15 2021-07-12 2023-09-28\n", - "6 sgp E9 2021-07-04 2023-09-28\n", - "7 sgp E31 2020-06-08 2021-09-20\n", - "8 sgp E32 2021-07-03 2023-10-31\n", - "9 sgp E33 2020-06-11 2023-10-31\n", - "10 sgp E34 2020-06-18 2023-09-28\n", - "11 sgp E35 2019-06-24 2023-09-28\n", - "12 sgp E36 2020-06-26 2023-09-28\n", - "13 sgp E37 2020-07-01 2023-10-31\n", - "14 sgp E38 2020-06-25 2021-05-29\n", - "15 sgp E39 2020-09-07 2023-10-30\n", - "16 sgp E40 2020-09-07 2023-09-28\n", - "17 sgp E41 2021-07-31 2023-08-02" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "print(\"The following locations and date ranges are available for this VAP:\")\n", - "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" - ] - }, - { - "cell_type": "markdown", - "id": "8d132223", - "metadata": {}, - "source": [ - "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "e563983a", - "metadata": {}, - "outputs": [], - "source": [ - "site_facility = ( 'sgp', 'E11' )\n", - "\n", - "date_start = '2023-09-25'\n", - "date_end = '2023-09-27'" - ] - }, - { - "cell_type": "markdown", - "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", - "metadata": {}, - "source": [ - "## Load data files\n", - "Load data files from /data/archive/" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'/data/archive/sgp/sgpradflux1longE11.c1'" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Compile list of files\n", - "site, facility = site_facility\n", - "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", - "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", - "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", - "dir_path\n" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "6be8f3dc", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "['20230925', '20230926', '20230927']" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "from datetime import date, timedelta\n", - "import pandas as pd\n", - "\n", - "def get_ARM_formated_dates(start_date, end_date):\n", - " \"\"\"\n", - " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", - " EXAMPLE:\n", - " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", - " >> [\"20180219\", \"20180220\", \"20180221\"] \n", - " \"\"\"\n", - " \n", - " _start_date = pd.to_datetime(start_date)\n", - " _end_date = pd.to_datetime(end_date)\n", - " \n", - " delta = _end_date - _start_date # returns timedelta \n", - " dates = []\n", - "\n", - " for i in range(delta.days + 1):\n", - " day = _start_date + timedelta(days=i)\n", - " day_formated = day.strftime(format=\"%Y%m%d\")\n", - " dates.append(day_formated)\n", - " return dates\n", - "\n", - "\n", - "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "51feea2e", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "['/data/archive/sgp/sgpradflux1longE11.c1/sgpradflux1longE11.c1.20230925.070000.nc',\n", - " '/data/archive/sgp/sgpradflux1longE11.c1/sgpradflux1longE11.c1.20230926.070000.nc',\n", - " '/data/archive/sgp/sgpradflux1longE11.c1/sgpradflux1longE11.c1.20230927.070000.nc']" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Filter a list of files based on date pattern\n", - "import glob\n", - "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", - "files_filter = []\n", - "for date in dates:\n", - " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", - " files_filter\n", - "files_filter" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "id": "b0e5d0d7", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "3 files loaded\n" - ] - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "
<xarray.Dataset>\n",
-       "Dimensions:                                        (time: 4320, bound: 2)\n",
-       "Coordinates:\n",
-       "  * time                                           (time) datetime64[ns] 2023...\n",
-       "Dimensions without coordinates: bound\n",
-       "Data variables: (12/54)\n",
-       "    base_time                                      (time) datetime64[ns] 2023...\n",
-       "    time_offset                                    (time) datetime64[ns] 2023...\n",
-       "    time_bounds                                    (time, bound) object dask.array<chunksize=(1440, 2), meta=np.ndarray>\n",
-       "    downwelling_shortwave                          (time) float32 dask.array<chunksize=(1440,), meta=np.ndarray>\n",
-       "    source_downwelling_shortwave                   (time) int32 dask.array<chunksize=(1440,), meta=np.ndarray>\n",
-       "    qc_downwelling_shortwave                       (time) int32 dask.array<chunksize=(1440,), meta=np.ndarray>\n",
-       "    ...                                             ...\n",
-       "    qc_pressure                                    (time) int32 dask.array<chunksize=(1440,), meta=np.ndarray>\n",
-       "    precipitation                                  (time) float32 dask.array<chunksize=(1440,), meta=np.ndarray>\n",
-       "    qc_precipitation                               (time) int32 dask.array<chunksize=(1440,), meta=np.ndarray>\n",
-       "    lat                                            (time) float32 36.88 ... 3...\n",
-       "    lon                                            (time) float32 -98.29 ... ...\n",
-       "    alt                                            (time) float32 360.0 ... 3...\n",
-       "Attributes: (12/21)\n",
-       "    command_line:            radflux1long -s sgp -f E11 -b 20230901 -e 202310...\n",
-       "    Conventions:             ARM-1.3\n",
-       "    process_version:         radflux1long-3.16.0\n",
-       "    dod_version:             radflux1long-c1-1.6\n",
-       "    input_datastreams:       sgpqcrad1longE11.c1 : 6.6 : 20230629.000000-2023...\n",
-       "    site_id:                 sgp\n",
-       "    ...                      ...\n",
-       "    fitmode_comment:         01 = daily_fit 00 =  1_fit\n",
-       "    history:                 created by user dsmgr on machine prod-proc2.adc....\n",
-       "    _file_dates:             ['20230925', '20230926', '20230927']\n",
-       "    _file_times:             ['070000', '070000', '070000']\n",
-       "    _datastream:             sgpradflux1longE11.c1\n",
-       "    _arm_standards_flag:     1
" - ], - "text/plain": [ - "\n", - "Dimensions: (time: 4320, bound: 2)\n", - "Coordinates:\n", - " * time (time) datetime64[ns] 2023...\n", - "Dimensions without coordinates: bound\n", - "Data variables: (12/54)\n", - " base_time (time) datetime64[ns] 2023...\n", - " time_offset (time) datetime64[ns] 2023...\n", - " time_bounds (time, bound) object dask.array\n", - " downwelling_shortwave (time) float32 dask.array\n", - " source_downwelling_shortwave (time) int32 dask.array\n", - " qc_downwelling_shortwave (time) int32 dask.array\n", - " ... ...\n", - " qc_pressure (time) int32 dask.array\n", - " precipitation (time) float32 dask.array\n", - " qc_precipitation (time) int32 dask.array\n", - " lat (time) float32 36.88 ... 3...\n", - " lon (time) float32 -98.29 ... ...\n", - " alt (time) float32 360.0 ... 3...\n", - "Attributes: (12/21)\n", - " command_line: radflux1long -s sgp -f E11 -b 20230901 -e 202310...\n", - " Conventions: ARM-1.3\n", - " process_version: radflux1long-3.16.0\n", - " dod_version: radflux1long-c1-1.6\n", - " input_datastreams: sgpqcrad1longE11.c1 : 6.6 : 20230629.000000-2023...\n", - " site_id: sgp\n", - " ... ...\n", - " fitmode_comment: 01 = daily_fit 00 = 1_fit\n", - " history: created by user dsmgr on machine prod-proc2.adc....\n", - " _file_dates: ['20230925', '20230926', '20230927']\n", - " _file_times: ['070000', '070000', '070000']\n", - " _datastream: sgpradflux1longE11.c1\n", - " _arm_standards_flag: 1" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Load files as a single dataset\n", - "files_list = files_filter \n", - "ds = act.io.armfiles.read_netcdf(files_list)\n", - "ds.clean.cleanup()\n", - "print(f'{len(files_list)} files loaded')\n", - "ds\n" - ] - }, - { - "cell_type": "markdown", - "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", - "metadata": {}, - "source": [ - "## Plot time series data\n", - "#### Define the list of variables to be plotted:" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "id": "d09b789e-84f1-4605-846b-a72c110c8048", - "metadata": {}, - "outputs": [], - "source": [ - "variables_to_plot = ['downwelling_shortwave', 'clearsky_downwelling_shortwave', 'downwelling_longwave']" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", - "metadata": {}, - "outputs": [], - "source": [ - "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", - "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", - "\n", - "for i,v in enumerate(variables_to_plot):\n", - " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", - " ts_ax.grid()\n", - "\n", - "plt.show()\n" - ] - }, - { - "cell_type": "markdown", - "id": "194399aa-1907-452b-8ba9-bc31d7f60291", - "metadata": {}, - "source": [ - "## Quality check plots\n", - "#### Define variable for QC plot" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", - "metadata": {}, - "outputs": [], - "source": [ - "qc_variable = 'downwelling_shortwave'" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", - "metadata": {}, - "outputs": [], - "source": [ - "# QC Plot\n", - "if ('qc_'+qc_variable) in ds.variables:\n", - "\n", - " # Plot\n", - " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", - " qc_display.add_subplots((2,), figsize = (9.5,10))\n", - " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", - " qc_ax.grid()\n", - " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", - "\n", - " plt.show()\n", - "else:\n", - " print(f'QC not available for the selected field: {qc_variable}')\n" - ] - }, - { - "cell_type": "markdown", - "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", - "metadata": {}, - "source": [ - "## Field selection dropdown menu\n", - "Select variable to be plotted from a dropdown menu" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", - "metadata": {}, - "outputs": [], - "source": [ - "plt.ioff()\n", - "\n", - "# populate dropdown menu with available variables \n", - "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", - "d_variable = 'downwelling_shortwave'\n", - "dropdown = widgets.Dropdown(\n", - " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", - " value= d_variable,\n", - " description='Field:',\n", - " disabled=False,\n", - ")\n", - "dropdown.layout.margin = '0px 30% 0px 20%'\n", - "dropdown.layout.width = '50%'\n", - "\n", - "# set up display\n", - "i_display = act.plotting.TimeSeriesDisplay(ds)\n", - "i_display.add_subplots((1,), figsize = (9.5,5))\n", - "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", - "i_ax.grid()\n", - "i_fig = i_display.fig\n", - "\n", - "# update plot callback function\n", - "def update_plot(change):\n", - " i_ax.cla()\n", - " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", - " i_ax_new.grid()\n", - " i_fig.canvas.draw()\n", - " i_fig.canvas.flush_events()\n", - "\n", - "dropdown.observe(update_plot, names='value')\n", - "\n", - "widgets.AppLayout(\n", - " header=dropdown,\n", - " center=i_fig.canvas,\n", - " pane_heights=[1, 6,1]\n", - ")\n" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.12" - }, - "widgets": { - "application/vnd.jupyter.widget-state+json": { - "state": {}, - "version_major": 2, - "version_minor": 0 - } - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/VAPs/quicklook/SACRADV3D3C/.ipynb_checkpoints/kasacradv3d3c.c1-checkpoint.ipynb b/VAPs/quicklook/SACRADV3D3C/.ipynb_checkpoints/kasacradv3d3c.c1-checkpoint.ipynb deleted file mode 100644 index 8d364ba7..00000000 --- a/VAPs/quicklook/SACRADV3D3C/.ipynb_checkpoints/kasacradv3d3c.c1-checkpoint.ipynb +++ /dev/null @@ -1,2574 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "70840257-70e4-45e2-b491-14bff5a257a3", - "metadata": {}, - "source": [ - "# KASACRADV3D3C.C1 Plots\n", - "\n", - "[Click here](https://www.arm.gov/capabilities/vaps/sacradv3d3c) for more information about this vap." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "460fd89f-e034-452c-b837-f65c5958264f", - "metadata": {}, - "outputs": [], - "source": [ - "%matplotlib widget\n", - "import ipywidgets as widgets\n", - "\n", - "import matplotlib.pyplot as plt\n", - "import ipywidgets as widgets\n", - "import numpy as np\n", - "import pandas as pd\n", - "import os\n", - "from datetime import datetime\n", - "\n", - "import act\n", - "import xarray as xr\n", - "\n", - "# Data archive directory\n", - "DATA_DIR = r'/data/archive/'\n", - "\n", - "# Datastream info\n", - "DATASTREAM_NAME = 'kasacradv3d3c'\n", - "DATA_LEVEL = 'c1'\n", - "LOCATIONS = [{'end_date': '2012-08-31', 'facility': 'C1', 'site': 'sgp', 'start_date': '2012-08-01'}]" - ] - }, - { - "cell_type": "markdown", - "id": "9faaf875", - "metadata": {}, - "source": [ - "## Define site, facility, and date range" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "ac6764f5", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "The following locations and date ranges are available for this VAP:\n" - ] - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
sitefacilitystart_dateend_date
0sgpC12012-08-012012-08-31
\n", - "
" - ], - "text/plain": [ - " site facility start_date end_date\n", - "0 sgp C1 2012-08-01 2012-08-31" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "print(\"The following locations and date ranges are available for this VAP:\")\n", - "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" - ] - }, - { - "cell_type": "markdown", - "id": "8d132223", - "metadata": {}, - "source": [ - "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "e563983a", - "metadata": {}, - "outputs": [], - "source": [ - "site_facility = ( 'sgp', 'C1' )\n", - "\n", - "date_start = '2012-08-29'\n", - "date_end = '2012-08-31'" - ] - }, - { - "cell_type": "markdown", - "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", - "metadata": {}, - "source": [ - "## Load data files\n", - "Load data files from /data/archive/" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'/data/archive/sgp/sgpkasacradv3d3cC1.c1'" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Compile list of files\n", - "site, facility = site_facility\n", - "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", - "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", - "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", - "dir_path\n" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "6be8f3dc", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "['20120829', '20120830', '20120831']" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "from datetime import date, timedelta\n", - "import pandas as pd\n", - "\n", - "def get_ARM_formated_dates(start_date, end_date):\n", - " \"\"\"\n", - " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", - " EXAMPLE:\n", - " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", - " >> [\"20180219\", \"20180220\", \"20180221\"] \n", - " \"\"\"\n", - " \n", - " _start_date = pd.to_datetime(start_date)\n", - " _end_date = pd.to_datetime(end_date)\n", - " \n", - " delta = _end_date - _start_date # returns timedelta \n", - " dates = []\n", - "\n", - " for i in range(delta.days + 1):\n", - " day = _start_date + timedelta(days=i)\n", - " day_formated = day.strftime(format=\"%Y%m%d\")\n", - " dates.append(day_formated)\n", - " return dates\n", - "\n", - "\n", - "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "51feea2e", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "['/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120829.025008.nc',\n", - " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120829.153009.nc',\n", - " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120829.201628.nc',\n", - " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120829.002402.nc',\n", - " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120829.175243.nc',\n", - " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120829.195648.nc',\n", - " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120829.130446.nc',\n", - " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120829.081435.nc',\n", - " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120829.151031.nc',\n", - " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120829.173303.nc',\n", - " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120829.030947.nc',\n", - " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120829.054747.nc',\n", - " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120829.124506.nc',\n", - " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120829.104113.nc',\n", - " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120829.075456.nc',\n", - " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120829.004340.nc',\n", - " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120829.102134.nc',\n", - " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120829.052807.nc',\n", - " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120829.221741.nc',\n", - " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120829.051559.nc',\n", - " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120829.223721.nc',\n", - " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120830.202247.nc',\n", - " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120830.104350.nc',\n", - " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120830.131114.nc',\n", - " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120830.030911.nc',\n", - " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120830.010234.nc',\n", - " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120830.173849.nc',\n", - " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120830.224833.nc',\n", - " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120830.004254.nc',\n", - " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120830.075837.nc',\n", - " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120830.081817.nc',\n", - " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120830.053252.nc',\n", - " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120830.125133.nc',\n", - " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120830.102411.nc',\n", - " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120830.153540.nc',\n", - " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120830.055231.nc',\n", - " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120830.151600.nc',\n", - " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120830.222853.nc',\n", - " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120830.175829.nc',\n", - " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120830.200306.nc',\n", - " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120830.032851.nc',\n", - " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120831.060543.nc',\n", - " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120831.054603.nc',\n", - " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120831.230808.nc',\n", - " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120831.202355.nc',\n", - " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120831.103937.nc',\n", - " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120831.130632.nc',\n", - " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120831.105916.nc',\n", - " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120831.224825.nc',\n", - " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120831.011234.nc',\n", - " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120831.175952.nc',\n", - " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120831.031811.nc',\n", - " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120831.033753.nc',\n", - " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120831.005255.nc',\n", - " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120831.132611.nc',\n", - " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120831.181932.nc',\n", - " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120831.153200.nc',\n", - " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120831.204334.nc',\n", - " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120831.083147.nc',\n", - " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120831.155139.nc',\n", - " '/data/archive/sgp/sgpkasacradv3d3cC1.c1/sgpkasacradv3d3cC1.c1.20120831.081207.nc']" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Filter a list of files based on date pattern\n", - "import glob\n", - "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", - "files_filter = []\n", - "for date in dates:\n", - " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", - " files_filter\n", - "files_filter" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "id": "b0e5d0d7", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "61 files loaded\n" - ] - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "
<xarray.Dataset>\n",
-       "Dimensions:                                       (time: 23687, bound: 2,\n",
-       "                                                   height: 201, bin: 28,\n",
-       "                                                   isoline: 4, h_distance: 401,\n",
-       "                                                   frequency: 1)\n",
-       "Coordinates:\n",
-       "  * time                                          (time) datetime64[ns] 2012-...\n",
-       "  * height                                        (height) float32 0.0 ... 10.0\n",
-       "  * bin                                           (bin) float32 -47.5 ... 20.0\n",
-       "  * isoline                                       (isoline) float32 5.0 ... 20.0\n",
-       "  * h_distance                                    (h_distance) float32 -1e+04...\n",
-       "  * frequency                                     (frequency) float32 3.529e+10\n",
-       "Dimensions without coordinates: bound\n",
-       "Data variables: (12/20)\n",
-       "    base_time                                     (time) datetime64[ns] 2012-...\n",
-       "    time_offset                                   (time) datetime64[ns] 2012-...\n",
-       "    time_bounds                                   (time, bound) object dask.array<chunksize=(392, 2), meta=np.ndarray>\n",
-       "    height_bounds                                 (time, height, bound) float32 dask.array<chunksize=(392, 201, 2), meta=np.ndarray>\n",
-       "    bin_bounds                                    (time, bin, bound) float32 dask.array<chunksize=(392, 28, 2), meta=np.ndarray>\n",
-       "    isoline_bounds                                (time, isoline, bound) float32 dask.array<chunksize=(392, 4, 2), meta=np.ndarray>\n",
-       "    ...                                            ...\n",
-       "    cloud_fraction                                (time, isoline, height) float32 dask.array<chunksize=(392, 4, 201), meta=np.ndarray>\n",
-       "    cloud_fraction_std                            (time, isoline, height) float32 dask.array<chunksize=(392, 4, 201), meta=np.ndarray>\n",
-       "    cfad                                          (time, bin, height) float32 dask.array<chunksize=(392, 28, 201), meta=np.ndarray>\n",
-       "    lat                                           (time) float32 36.6 ... 36.6\n",
-       "    lon                                           (time) float32 -97.49 ... -...\n",
-       "    alt                                           (time) float32 318.0 ... 318.0\n",
-       "Attributes: (12/20)\n",
-       "    command_line:          sacradv3d3c -s sgp -f C1 -b 20120829 -n sacradv3d3...\n",
-       "    process_version:       vap-sacradv3d3c-1.1-0.el6\n",
-       "    dod_version:           kasacradv3d3c-c1-1.2\n",
-       "    input_datastreams:     sgpkasacrcorcwrhiC1.c1 : 1.0 : 20120829.002403-201...\n",
-       "    site_id:               sgp\n",
-       "    platform_id:           kasacradv3d3c\n",
-       "    ...                    ...\n",
-       "    radar_beam_width_h:    0.311\n",
-       "    history:               created by user singh on machine amber at 2018-12-...\n",
-       "    _file_dates:           ['20120829', '20120829', '20120829', '20120829', '...\n",
-       "    _file_times:           ['002402', '004340', '025008', '030947', '051559',...\n",
-       "    _datastream:           sgpkasacradv3d3cC1.c1\n",
-       "    _arm_standards_flag:   1
" - ], - "text/plain": [ - "\n", - "Dimensions: (time: 23687, bound: 2,\n", - " height: 201, bin: 28,\n", - " isoline: 4, h_distance: 401,\n", - " frequency: 1)\n", - "Coordinates:\n", - " * time (time) datetime64[ns] 2012-...\n", - " * height (height) float32 0.0 ... 10.0\n", - " * bin (bin) float32 -47.5 ... 20.0\n", - " * isoline (isoline) float32 5.0 ... 20.0\n", - " * h_distance (h_distance) float32 -1e+04...\n", - " * frequency (frequency) float32 3.529e+10\n", - "Dimensions without coordinates: bound\n", - "Data variables: (12/20)\n", - " base_time (time) datetime64[ns] 2012-...\n", - " time_offset (time) datetime64[ns] 2012-...\n", - " time_bounds (time, bound) object dask.array\n", - " height_bounds (time, height, bound) float32 dask.array\n", - " bin_bounds (time, bin, bound) float32 dask.array\n", - " isoline_bounds (time, isoline, bound) float32 dask.array\n", - " ... ...\n", - " cloud_fraction (time, isoline, height) float32 dask.array\n", - " cloud_fraction_std (time, isoline, height) float32 dask.array\n", - " cfad (time, bin, height) float32 dask.array\n", - " lat (time) float32 36.6 ... 36.6\n", - " lon (time) float32 -97.49 ... -...\n", - " alt (time) float32 318.0 ... 318.0\n", - "Attributes: (12/20)\n", - " command_line: sacradv3d3c -s sgp -f C1 -b 20120829 -n sacradv3d3...\n", - " process_version: vap-sacradv3d3c-1.1-0.el6\n", - " dod_version: kasacradv3d3c-c1-1.2\n", - " input_datastreams: sgpkasacrcorcwrhiC1.c1 : 1.0 : 20120829.002403-201...\n", - " site_id: sgp\n", - " platform_id: kasacradv3d3c\n", - " ... ...\n", - " radar_beam_width_h: 0.311\n", - " history: created by user singh on machine amber at 2018-12-...\n", - " _file_dates: ['20120829', '20120829', '20120829', '20120829', '...\n", - " _file_times: ['002402', '004340', '025008', '030947', '051559',...\n", - " _datastream: sgpkasacradv3d3cC1.c1\n", - " _arm_standards_flag: 1" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Load files as a single dataset\n", - "files_list = files_filter \n", - "ds = act.io.armfiles.read_netcdf(files_list)\n", - "ds.clean.cleanup()\n", - "print(f'{len(files_list)} files loaded')\n", - "ds\n" - ] - }, - { - "cell_type": "markdown", - "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", - "metadata": {}, - "source": [ - "## Plot time series data\n", - "#### Define the list of variables to be plotted:" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "id": "d09b789e-84f1-4605-846b-a72c110c8048", - "metadata": {}, - "outputs": [], - "source": [ - "variables_to_plot = ['wind_speed', 'wind_direction', 'cloud_fraction']" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", - "metadata": {}, - "outputs": [ - { - "ename": "TypeError", - "evalue": "Dimensions of C (201, 4, 23687) should be one smaller than X(23687) and Y(4) while using shading='flat' see help(pcolormesh)", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", - "Cell \u001b[0;32mIn[9], line 5\u001b[0m\n\u001b[1;32m 2\u001b[0m ts_display\u001b[38;5;241m.\u001b[39madd_subplots((\u001b[38;5;28mlen\u001b[39m(variables_to_plot),), figsize \u001b[38;5;241m=\u001b[39m (\u001b[38;5;241m9.5\u001b[39m,\u001b[38;5;241m4\u001b[39m\u001b[38;5;241m*\u001b[39m\u001b[38;5;28mlen\u001b[39m(variables_to_plot)))\n\u001b[1;32m 4\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m i,v \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28menumerate\u001b[39m(variables_to_plot):\n\u001b[0;32m----> 5\u001b[0m ts_ax \u001b[38;5;241m=\u001b[39m \u001b[43mts_display\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mplot\u001b[49m\u001b[43m(\u001b[49m\u001b[43mv\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43msubplot_index\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mi\u001b[49m\u001b[43m,\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mset_title\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mds\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mvariables\u001b[49m\u001b[43m[\u001b[49m\u001b[43mv\u001b[49m\u001b[43m]\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mattrs\u001b[49m\u001b[43m[\u001b[49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43mlong_name\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m,\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 6\u001b[0m ts_ax\u001b[38;5;241m.\u001b[39mgrid()\n\u001b[1;32m 8\u001b[0m plt\u001b[38;5;241m.\u001b[39mshow()\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/act/plotting/timeseriesdisplay.py:588\u001b[0m, in \u001b[0;36mTimeSeriesDisplay.plot\u001b[0;34m(self, field, dsname, subplot_index, cmap, set_title, add_nan, day_night_background, invert_y_axis, abs_limits, time_rng, y_rng, use_var_for_y, set_shading, assessment_overplot, overplot_marker, overplot_behind, overplot_markersize, assessment_overplot_category, assessment_overplot_category_color, force_line_plot, labels, cbar_label, cbar_h_adjust, secondary_y, y_axis_flag_meanings, colorbar_labels, cb_friendly, **kwargs)\u001b[0m\n\u001b[1;32m 586\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124medgecolors\u001b[39m\u001b[38;5;124m'\u001b[39m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;129;01min\u001b[39;00m kwargs\u001b[38;5;241m.\u001b[39mkeys():\n\u001b[1;32m 587\u001b[0m kwargs[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124medgecolors\u001b[39m\u001b[38;5;124m'\u001b[39m] \u001b[38;5;241m=\u001b[39m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mface\u001b[39m\u001b[38;5;124m'\u001b[39m\n\u001b[0;32m--> 588\u001b[0m mesh \u001b[38;5;241m=\u001b[39m \u001b[43max\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mpcolormesh\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 589\u001b[0m \u001b[43m \u001b[49m\u001b[43mnp\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43masarray\u001b[49m\u001b[43m(\u001b[49m\u001b[43mxdata\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 590\u001b[0m \u001b[43m \u001b[49m\u001b[43mydata\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 591\u001b[0m \u001b[43m \u001b[49m\u001b[43mdata\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mtranspose\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 592\u001b[0m \u001b[43m \u001b[49m\u001b[43mshading\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mset_shading\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 593\u001b[0m \u001b[43m \u001b[49m\u001b[43mcmap\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcmap\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 594\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 595\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 597\u001b[0m \u001b[38;5;66;03m# Set Title\u001b[39;00m\n\u001b[1;32m 598\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m set_title \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/matplotlib/__init__.py:1442\u001b[0m, in \u001b[0;36m_preprocess_data..inner\u001b[0;34m(ax, data, *args, **kwargs)\u001b[0m\n\u001b[1;32m 1439\u001b[0m \u001b[38;5;129m@functools\u001b[39m\u001b[38;5;241m.\u001b[39mwraps(func)\n\u001b[1;32m 1440\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21minner\u001b[39m(ax, \u001b[38;5;241m*\u001b[39margs, data\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mNone\u001b[39;00m, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs):\n\u001b[1;32m 1441\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m data \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[0;32m-> 1442\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mfunc\u001b[49m\u001b[43m(\u001b[49m\u001b[43max\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;28;43mmap\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43msanitize_sequence\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43margs\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 1444\u001b[0m bound \u001b[38;5;241m=\u001b[39m new_sig\u001b[38;5;241m.\u001b[39mbind(ax, \u001b[38;5;241m*\u001b[39margs, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs)\n\u001b[1;32m 1445\u001b[0m auto_label \u001b[38;5;241m=\u001b[39m (bound\u001b[38;5;241m.\u001b[39marguments\u001b[38;5;241m.\u001b[39mget(label_namer)\n\u001b[1;32m 1446\u001b[0m \u001b[38;5;129;01mor\u001b[39;00m bound\u001b[38;5;241m.\u001b[39mkwargs\u001b[38;5;241m.\u001b[39mget(label_namer))\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/matplotlib/axes/_axes.py:6220\u001b[0m, in \u001b[0;36mAxes.pcolormesh\u001b[0;34m(self, alpha, norm, cmap, vmin, vmax, shading, antialiased, *args, **kwargs)\u001b[0m\n\u001b[1;32m 6217\u001b[0m shading \u001b[38;5;241m=\u001b[39m shading\u001b[38;5;241m.\u001b[39mlower()\n\u001b[1;32m 6218\u001b[0m kwargs\u001b[38;5;241m.\u001b[39msetdefault(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124medgecolors\u001b[39m\u001b[38;5;124m'\u001b[39m, \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mnone\u001b[39m\u001b[38;5;124m'\u001b[39m)\n\u001b[0;32m-> 6220\u001b[0m X, Y, C, shading \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_pcolorargs\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43mpcolormesh\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 6221\u001b[0m \u001b[43m \u001b[49m\u001b[43mshading\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mshading\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mkwargs\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 6222\u001b[0m coords \u001b[38;5;241m=\u001b[39m np\u001b[38;5;241m.\u001b[39mstack([X, Y], axis\u001b[38;5;241m=\u001b[39m\u001b[38;5;241m-\u001b[39m\u001b[38;5;241m1\u001b[39m)\n\u001b[1;32m 6223\u001b[0m \u001b[38;5;66;03m# convert to one dimensional array, except for 3D RGB(A) arrays\u001b[39;00m\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/matplotlib/axes/_axes.py:5751\u001b[0m, in \u001b[0;36mAxes._pcolorargs\u001b[0;34m(self, funcname, shading, *args, **kwargs)\u001b[0m\n\u001b[1;32m 5749\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m shading \u001b[38;5;241m==\u001b[39m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mflat\u001b[39m\u001b[38;5;124m'\u001b[39m:\n\u001b[1;32m 5750\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m (Nx, Ny) \u001b[38;5;241m!=\u001b[39m (ncols \u001b[38;5;241m+\u001b[39m \u001b[38;5;241m1\u001b[39m, nrows \u001b[38;5;241m+\u001b[39m \u001b[38;5;241m1\u001b[39m):\n\u001b[0;32m-> 5751\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mTypeError\u001b[39;00m(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mDimensions of C \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mC\u001b[38;5;241m.\u001b[39mshape\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m should\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 5752\u001b[0m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m be one smaller than X(\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mNx\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m) and Y(\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mNy\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m)\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 5753\u001b[0m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m while using shading=\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mflat\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 5754\u001b[0m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m see help(\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mfuncname\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m)\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[1;32m 5755\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m: \u001b[38;5;66;03m# ['nearest', 'gouraud']:\u001b[39;00m\n\u001b[1;32m 5756\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m (Nx, Ny) \u001b[38;5;241m!=\u001b[39m (ncols, nrows):\n", - "\u001b[0;31mTypeError\u001b[0m: Dimensions of C (201, 4, 23687) should be one smaller than X(23687) and Y(4) while using shading='flat' see help(pcolormesh)" - ] - }, - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "8189dde8d6e0443cb4732d2453c2a30b", - "version_major": 2, - "version_minor": 0 - }, - "image/png": "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nEQigW3btsl6C763bds2mKbZ5d9+06ZN6eOT6ecpPObexHO88PEcp+7wHCdSK+B0BYhod/F4HOeffz4sy8JDDz3U6WczZszY7fmapnX6t0gmnNj1cTvPITm6+tvv6djs+hiPuffwHPcWnuP0dTzHidTjiC2Ry8TjcZx33nlYuXIlXnvttU69vF2prq7u1PMPAFu2bAGws8c303MCgQB69eqVx9rTrnr37g3DMLr821dVVaG6uhoAMv48Ex7zwsZz3Dt4jlNXeI4TOYOBLZGLpG6GX3zxBV5//XVbN6tjjz0W8+bNQywWSz/26quvoqamBgMHDkw/57XXXuv0e6+++ipGjRqFYDCY1/dAO4VCIYwcOXK3v/1rr72G4447DoMGDUJ1dXWnn8diMcydOxfHHXdcxtflMS9cPMe9hec4fR3PcSIHOZGxisivmpqaxEcffSQ++ugjAUDcd9994qOPPhKrV68W8XhcnH766WLAgAFi8eLFYuPGjemvaDSafo2pU6eKb3/72+l/19fXi6qqKnHBBReIJUuWiH/+85+ivLy8y20CbrjhBrFs2TLxv//7v9wmQJHUViD/+7//K5YtWyauv/56UVJSIlatWiWE6NgKpKKiQvzzn/8US5YsERdccMFuW4HwmBcOnuP+w3PcX3iOE7kXA1sihWbPni0A7PZ16aWXipUrV3b5MwBi9uzZ6de49NJLxdixYzu97ieffCLGjBkjwuGwqK6uFtOnT09vEZAyZ84cccQRR4hQKCQGDhwoHn74YQXvmIQQ4g9/+IPYd999RSgUEiNGjBBz585N/8yyLHHbbbeJ6upqEQ6HxQknnCCWLFnS6fd5zAsHz3F/4jnuHzzHidxLEyK58pyIiIiIiIioAHGNLRERERERERU0BrZERERERERU0BjYEhERERERUUFjYEtEREREREQFjYEtERERERERFTQGtkRERERERFTQGNgSERERERFRQWNgS0RERERERAWNgS0REREREREVNAa2REREREREVNAY2BIREREREVFBY2BLREREREREBY2BLRERERERERU0BrZEROR6zzzzDDRNw5NPPrnbzw477DBomoZXXnllt58NHjwYI0aMAADMmTMHmqZhzpw5eavXqlWroGkaHn/88Zx+//HHH4emaVi1alX6sXHjxmHcuHF5qV82NmzYgOnTp2Px4sW7/Wz69OnQNE15nYiIiOxiYEtERK43btw4aJqG2bNnd3q8rq4OS5YsQUlJyW4/W7duHb766iuMHz8eADBixAi888476UDXrR566CE89NBDysvdsGEDbr/99i4D2yuvvBLvvPOO8joRERHZFXC6AkRERHvSu3dvHHLIIbuNts6dOxeBQADf+c53dgtsU/9OBbbl5eU45phjlNR3bxx00EF7fI5pmkgkEgiHwwpqBAwYMAADBgxQUhYREVEuOGJLREQFYfz48Vi+fDk2btyYfmzOnDk48sgjMXnyZCxatAhNTU2dfmYYBsaMGZP+99enIl922WUoLS3Fl19+icmTJ6O0tBS1tbX48Y9/jGg02qn8DRs24LzzzkNZWRkqKiowZcoUbNq0yXb93333XYwePRqRSAQ1NTWYNm0a4vH4bs/7+lTk1HTn3/zmN/jlL3+JQYMGIRwOpwP3Dz74AKeffjoqKysRiURwxBFH4KmnntrtddevX4+rr74atbW1CIVCqKmpwTnnnIPNmzen/44AcPnll0PTNGiahunTpwPoeiqyZVn4zW9+g2HDhiEcDqNv37645JJLsG7dut3ezyGHHIKFCxdizJgxKC4uxn777Ye77roLlmXZ/vsRERF1h4EtEREVhNTI666B6ezZszF27FiMHj0amqZh/vz5nX42YsQIVFRUdPu68Xgcp59+OiZMmIAXXngBV1xxBe6//37cfffd6ee0tbVh4sSJePXVVzFjxgw8/fTTqK6uxpQpU2zVfdmyZZgwYQLq6+vx+OOP45FHHsFHH32EX/7yl7bf/+9+9zu8+eabuPfee/Gf//wHw4YNw+zZszF69GjU19fjkUcewQsvvIDDDz8cU6ZM6bTud/369TjyyCPx3HPP4cYbb8R//vMfPPDAA6ioqMCOHTswYsQIzJw5EwBwyy234J133sE777yDK6+8MmN9vve97+Gmm27CSSedhBdffBF33nknXn75ZRx33HHYtm1bp+du2rQJF110ES6++GK8+OKL+MY3voFp06bhr3/9q+33T0RE1C1BRERUAOrq6oSu6+Lqq68WQgixbds2oWmaePnll4UQQhx11FHiJz/5iRBCiDVr1ggA4mc/+1n692fPni0AiNmzZ6cfu/TSSwUA8dRTT3Uqa/LkyWLo0KHpfz/88MMCgHjhhRc6Pe+qq64SAMTMmTO7rfuU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aSb309ChQ9WMGTNUQ0ND+2PTpk1Gt7dUJvfTn/70J9WnTx916623qrfeeku9+eab6o9//KPR7S2FqX2UTqc7fIYaGhrUueeeq3bbbTflOI7x7S6Wyc/SnnvuqU444QT173//W7399tvq/PPPV1VVVaqhocHoNpfC1H5atWqV6tOnj5o0aZJ688031YsvvqjGjBmjTj75ZOPbXKxy7aNf/OIXaubMmWrmzJlKRNQnn3yyXVlBvv/2KwLbgFi7dq0SEfXMM88opZRyHEfV1dWp6667rv01ra2tqqamRt12221KKaU2btyoEomEeuihh9pfs2rVKmXbtvrnP/+plFJq2bJlSkTU888/3/6ahQsXKhFRb775polNKytd+2lbs2bNClRg+1km9lHBz3/+czVs2DBNW6KXyf30l7/8RVmWpTKZjKat0UP3PpozZ47ad9991euvvx6owPazdO6noUOHql/84hdmNkQzXfspm82qIUOGqLvuusvg1uhh6rqUyWTUwIED1YwZMzRujT669tPHH3+sRETNmzev/TVNTU1KRNSTTz5pYtPKStd+uv3229XAgQNVPp9vf83ixYuViKh33nnHxKaVTSn7aFtPPfVUp4Ft2O6//YJU5IBobGwUEZG+ffuKiMjy5ctlzZo1Mn78+PbXpFIpGTdunCxYsEBERBYtWiTZbLbDawYPHiwjR45sf83ChQulpqZGDjnkkPbXHHrooVJTU9P+miDRtZ/CxOQ+amxsbC8naEztpw0bNsgDDzwgY8aMkUQioWtztNC5jz766CP57ne/K7/97W+lqqrKxOZoo/uzdP3110u/fv3kc5/7nPzsZz8LTPr/Z+naT6+88oqsWrVKbNuWz3/+8zJo0CA5/vjjt0sdDAJT16XHHntM1q1bJ2effbamLdFL137q16+f7LfffnL//ffLli1bJJfLye233y61tbUyatQoU5tXNrr2UzqdlmQyKbb9aZhRWVkpIiLz58/Xu1FlVso+ciNs999+QWAbAEopmTJlinzxi1+UkSNHiojImjVrRESktra2w2tra2vb/2/NmjWSTCalT58+3b5m4MCB25U5cODA9tcEhc79FBYm99G7774rN998s0yaNKncm6Gdif102WWXSY8ePaRfv36yYsUK+ctf/qJrc7TQuY+UUnL22WfLpEmTZPTo0bo3RSvdn6WLLrpIHnroIXnqqafkBz/4gdx0001y/vnn69wkLXTup/fee09ERKZPny4//vGP5W9/+5v06dNHxo0bJxs2bNC6XeVk8vp99913y7HHHiv19fXl3gztdO4ny7Jk7ty5snjxYunVq5dUVFTIL37xC/nnP/8ZqLk3RPTup6OOOkrWrFkjN9xwg2QyGfnkk0/kiiuuEBGRhoYGrdtVTqXuIzfCdP/tJwS2AfCDH/xAXn31VXnwwQe3+z/Lsjr8rpTa7rnP+uxrOnu9m/fxG937KQxM7aPVq1fLcccdJ9/61rfk3HPP3blKe8DEfrr00ktl8eLF8sQTT0gsFpMJEyaIUmrnK2+Izn108803S1NTk0ydOrV8FfaI7s/SxRdfLOPGjZMDDjhAzj33XLntttvk7rvvlvXr15dnAwzRuZ8cxxERkSuvvFJOPvlkGTVqlMyaNUssy5I//vGPZdoC/Uxdvz/88EN5/PHH5Zxzztm5CntE535SSsn5558vAwcOlGeffVZefPFF+frXvy4nnnhioAI2Eb37acSIEXLffffJ//7v/0pVVZXU1dXJ7rvvLrW1tRKLxcq3EZqVex/t6D1KfR98isDW5374wx/KY489Jk899ZTssssu7c/X1dWJiGzXqrN27dr2VqS6urr2lrLuXvPRRx9tV+7HH3+8XWuUn+neT2Fgah+tXr1ajjzySDnssMPkjjvu0LEpWpnaT/3795e9995bjjnmGHnooYdkzpw58vzzz+vYpLLTvY/+9a9/yfPPPy+pVEri8bjsueeeIiIyevRoOeuss7RtV7l5cV069NBDRUTkP//5T1m2wQTd+2nQoEEiIjJ8+PD2/0+lUrL77rvLihUryr9BGpj8LM2aNUv69esnX/va18q9GdqZuDb97W9/k4ceekjGjh0rBx10kNxyyy1SWVkp9913n85NKysTn6fTTz9d1qxZI6tWrZL169fL9OnT5eOPP5Zhw4bp2qyy2pl95EZY7r99R+sIXpTMcRx1wQUXqMGDB6u333670/+vq6tT119/fftz6XS60wH+s2fPbn/N6tWrO5086oUXXmh/zfPPPx+Yweum9tO2gjZ5lMl99OGHH6q99tpLnXrqqZ3OcOtnXnyWClasWKFERD311FPl2yANTO2jDz74QC1durT98fjjjysRUX/605/UypUrNW/lzvPys/TXv/5ViYj64IMPyrhFepjaT42NjSqVSnWYPKowOdLtt9+ua/PKwvRnyXEcNWzYsMDMZl9gaj899thjyrbt7WYe33vvvdXPfvYzHZtWVl5em+6++25VVVXV6ezAflKOfbStHU0eFdT7b78isPWp73//+6qmpkY9/fTTHabfb25ubn/Nddddp2pqatQjjzyili5dqk477bROp2TfZZdd1JNPPqleeeUVddRRR3W63M8BBxygFi5cqBYuXKj233//wEw3bnI/ffDBB2rx4sXq6quvVj179lSLFy9Wixcv9v3SGqb20apVq9See+6pjjrqKPXhhx92KCsITO2nF154Qd18881q8eLF6v3331f/+te/1Be/+EW1xx57qNbWVuPbXQyT59u2li9fHqhZkU3tpwULFqgbb7xRLV68WL333ntq9uzZavDgweprX/ua8W0uhcnP00UXXaSGDBmiHn/8cfXmm2+qc845Rw0cOFBt2LDB6DYXy/Q59+STTyoRUcuWLTO2jeVgaj99/PHHql+/fuqb3/ymWrJkiXrrrbfUJZdcohKJhFqyZInx7S6Wyc/TzTffrBYtWqTeeust9etf/1pVVlaqX/7yl0a3txTl2kcNDQ1q8eLF6s4772yfSXvx4sVq/fr17a8J8v23XxHY+pSIdPqYNWtW+2scx1HTpk1TdXV1KpVKqS996Utq6dKlHd6npaVF/eAHP1B9+/ZVlZWV6sQTT1QrVqzo8Jr169erM844Q/Xq1Uv16tVLnXHGGb5vUSswuZ/OOuusTsvyey+bqX00a9asLssKAlP76dVXX1VHHnmk6tu3r0qlUmq33XZTkyZNUh9++KGpTS2ZyfNtW0ELbE3tp0WLFqlDDjlE1dTUqIqKCrXPPvuoadOmqS1btpja1J1i8vOUyWTUj370IzVw4EDVq1cvdfTRR6vXXnvNxGbuFNPn3GmnnabGjBmje7PKzuR+eumll9T48eNV3759Va9evdShhx6q5syZY2Izd5rJ/XTmmWeqvn37qmQyqQ444AB1//33m9jEnVaufTRt2rQdvk+Q77/9ylIqQLOVAAAAAADwGUweBQAAAAAINAJbAAAAAECgEdgCAAAAAAKNwBYAAAAAEGgEtgAAAACAQCOwBQAAAAAEGoEtAAAAACDQCGwBAAAAAIFGYAsAAAAACDQCWwAAAABAoBHYAgAAAAACjcAWAAAAABBoBLYAAAAAgEAjsAUAAAAABBqBLQAAAAAg0AhsAQAAAACBRmALAAAAAAg0AlsAAAAAQKAR2AIAAAAAAo3AFgAAAAAQaAS2AAAAAIBAI7AFAAAAAAQagS0AAAAAINAIbAEAAAAAgUZgCwAAAAAINAJbAAAAAECgEdgCAAAAAAKNwBYAAAAAEGgEtgAAAACAQCOwBQAAAAAEGoEtAAAAACDQCGwBAAAAAIFGYAsAAAAACDQCWwAAAABAoBHYAgAAAAACjcAWAAAAABBoBLYAAAAAgEAjsAUAAAAABBqBLQAAAAAg0AhsAQAAAACBRmALAAAAAAg0AlsAAAAAQKAR2AIAAAAAAo3AFgAAAAAQaAS2AAAAAIBAI7AFAAAAAAQagS0AAAAAINAIbAEAAAAAgUZgCwAAAAAINAJbAAAAAECgEdgCAAAAAAKNwBYAAAAAEGgEtgAAAACAQCOwBQAAAAAEGoEtAAAAACDQCGwBAAAAAIFGYAsAAAAACDQCWwAAAABAoBHYAgAAAAACjcAWAAAAABBoBLYAAAAAgEAjsAUAAAAABBqBLQAAAAAg0AhsAQAAAACBRmALAAAAAAg0AlsAAAAAQKAR2AIAAOD/t18HJAAAAACC/r9uR6AvBFgTWwAAANbEFgAAgDWxBQAAYE1sAQAAWBNbAAAA1sQWAACANbEFAABgTWwBAABYE1sAAADWxBYAAIA1sQUAAGBNbAEAAFgTWwAAANbEFgAAgDWxBQAAYE1sAQAAWBNbAAAA1sQWAACANbEFAABgTWwBAABYE1sAAADWxBYAAIA1sQUAAGBNbAEAAFgTWwAAANbEFgAAgDWxBQAAYE1sAQAAWBNbAAAA1sQWAACANbEFAABgTWwBAABYE1sAAADWxBYAAIA1sQUAAGBNbAEAAFgTWwAAANbEFgAAgDWxBQAAYE1sAQAAWBNbAAAA1sQWAACANbEFAABgLRIMdYlK/mrBAAAAAElFTkSuQmCC", - "text/html": [ - "\n", - "
\n", - "
\n", - " Figure\n", - "
\n", - " \n", - "
\n", - " " - ], - "text/plain": [ - "Canvas(toolbar=Toolbar(toolitems=[('Home', 'Reset original view', 'home', 'home'), ('Back', 'Back to previous …" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", - "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", - "\n", - "for i,v in enumerate(variables_to_plot):\n", - " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", - " ts_ax.grid()\n", - "\n", - "plt.show()\n" - ] - }, - { - "cell_type": "markdown", - "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", - "metadata": {}, - "source": [ - "## Field selection dropdown menu\n", - "Select variable to be plotted from a dropdown menu" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", - "metadata": {}, - "outputs": [], - "source": [ - "plt.ioff()\n", - "\n", - "# populate dropdown menu with available variables \n", - "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", - "d_variable = 'wind_speed'\n", - "dropdown = widgets.Dropdown(\n", - " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", - " value= d_variable,\n", - " description='Field:',\n", - " disabled=False,\n", - ")\n", - "dropdown.layout.margin = '0px 30% 0px 20%'\n", - "dropdown.layout.width = '50%'\n", - "\n", - "# set up display\n", - "i_display = act.plotting.TimeSeriesDisplay(ds)\n", - "i_display.add_subplots((1,), figsize = (9.5,5))\n", - "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", - "i_ax.grid()\n", - "i_fig = i_display.fig\n", - "\n", - "# update plot callback function\n", - "def update_plot(change):\n", - " i_ax.cla()\n", - " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", - " i_ax_new.grid()\n", - " i_fig.canvas.draw()\n", - " i_fig.canvas.flush_events()\n", - "\n", - "dropdown.observe(update_plot, names='value')\n", - "\n", - "widgets.AppLayout(\n", - " header=dropdown,\n", - " center=i_fig.canvas,\n", - " pane_heights=[1, 6,1]\n", - ")\n" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.16" - }, - "widgets": { - "application/vnd.jupyter.widget-state+json": { - "state": {}, - "version_major": 2, - "version_minor": 0 - } - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/VAPs/quicklook/SFCCLDGRID/.ipynb_checkpoints/15swfcldgrid1long.c1-checkpoint.ipynb b/VAPs/quicklook/SFCCLDGRID/.ipynb_checkpoints/15swfcldgrid1long.c1-checkpoint.ipynb deleted file mode 100644 index 0ed9cccf..00000000 --- a/VAPs/quicklook/SFCCLDGRID/.ipynb_checkpoints/15swfcldgrid1long.c1-checkpoint.ipynb +++ /dev/null @@ -1,2384 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "70840257-70e4-45e2-b491-14bff5a257a3", - "metadata": {}, - "source": [ - "# 15SWFCLDGRID1LONG.C1 Plots\n", - "\n", - "[Click here](https://www.arm.gov/capabilities/vaps/sfccldgrid) for more information about this vap." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "460fd89f-e034-452c-b837-f65c5958264f", - "metadata": {}, - "outputs": [], - "source": [ - "%matplotlib widget\n", - "import ipywidgets as widgets\n", - "\n", - "import matplotlib.pyplot as plt\n", - "import ipywidgets as widgets\n", - "import numpy as np\n", - "import pandas as pd\n", - "import os\n", - "from datetime import datetime\n", - "\n", - "import act\n", - "import xarray as xr\n", - "\n", - "# Data archive directory\n", - "DATA_DIR = r'/data/archive/'\n", - "\n", - "# Datastream info\n", - "DATASTREAM_NAME = '15swfcldgrid1long'\n", - "DATA_LEVEL = 'c1'\n", - "LOCATIONS = [{'end_date': '2009-11-25', 'facility': 'N1', 'site': 'sgp', 'start_date': '1997-01-01'}]" - ] - }, - { - "cell_type": "markdown", - "id": "9faaf875", - "metadata": {}, - "source": [ - "## Define site, facility, and date range" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "ac6764f5", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "The following locations and date ranges are available for this VAP:\n" - ] - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
sitefacilitystart_dateend_date
0sgpN11997-01-012009-11-25
\n", - "
" - ], - "text/plain": [ - " site facility start_date end_date\n", - "0 sgp N1 1997-01-01 2009-11-25" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "print(\"The following locations and date ranges are available for this VAP:\")\n", - "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" - ] - }, - { - "cell_type": "markdown", - "id": "8d132223", - "metadata": {}, - "source": [ - "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "e563983a", - "metadata": {}, - "outputs": [], - "source": [ - "site_facility = ( 'sgp', 'N1' )\n", - "\n", - "date_start = '2009-11-23'\n", - "date_end = '2009-11-25'" - ] - }, - { - "cell_type": "markdown", - "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", - "metadata": {}, - "source": [ - "## Load data files\n", - "Load data files from /data/archive/" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'/data/archive/sgp/sgp15swfcldgrid1longN1.c1'" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Compile list of files\n", - "site, facility = site_facility\n", - "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", - "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", - "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", - "dir_path\n" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "6be8f3dc", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "['20091123', '20091124', '20091125']" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "from datetime import date, timedelta\n", - "import pandas as pd\n", - "\n", - "def get_ARM_formated_dates(start_date, end_date):\n", - " \"\"\"\n", - " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", - " EXAMPLE:\n", - " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", - " >> [\"20180219\", \"20180220\", \"20180221\"] \n", - " \"\"\"\n", - " \n", - " _start_date = pd.to_datetime(start_date)\n", - " _end_date = pd.to_datetime(end_date)\n", - " \n", - " delta = _end_date - _start_date # returns timedelta \n", - " dates = []\n", - "\n", - " for i in range(delta.days + 1):\n", - " day = _start_date + timedelta(days=i)\n", - " day_formated = day.strftime(format=\"%Y%m%d\")\n", - " dates.append(day_formated)\n", - " return dates\n", - "\n", - "\n", - "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "51feea2e", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "['/data/archive/sgp/sgp15swfcldgrid1longN1.c1/sgp15swfcldgrid1longN1.c1.20091123.180000.cdf',\n", - " '/data/archive/sgp/sgp15swfcldgrid1longN1.c1/sgp15swfcldgrid1longN1.c1.20091124.144500.cdf',\n", - " '/data/archive/sgp/sgp15swfcldgrid1longN1.c1/sgp15swfcldgrid1longN1.c1.20091125.144500.cdf']" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Filter a list of files based on date pattern\n", - "import glob\n", - "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", - "files_filter = []\n", - "for date in dates:\n", - " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", - " files_filter\n", - "files_filter" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "id": "b0e5d0d7", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "3 files loaded\n" - ] - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "
<xarray.Dataset>\n",
-       "Dimensions:           (time: 33, lat: 15, lon: 17)\n",
-       "Coordinates:\n",
-       "  * time              (time) datetime64[ns] 2009-11-23T18:00:00 ... 2009-11-2...\n",
-       "  * lat               (lat) float32 38.5 38.25 38.0 37.75 ... 35.5 35.25 35.0\n",
-       "  * lon               (lon) float32 99.5 99.25 99.0 98.75 ... 96.0 95.75 95.5\n",
-       "Data variables: (12/22)\n",
-       "    base_time         (time) datetime64[ns] 2009-11-23T18:00:00 ... 2009-11-2...\n",
-       "    time_offset       (time) datetime64[ns] 2009-11-23T18:00:00 ... 2009-11-2...\n",
-       "    cloudfraction     (time, lat, lon) float32 dask.array<chunksize=(2, 15, 17), meta=np.ndarray>\n",
-       "    qc_cloudfraction  (time, lat, lon) int32 dask.array<chunksize=(2, 15, 17), meta=np.ndarray>\n",
-       "    cf_cloudfraction  (time) int32 dask.array<chunksize=(2,), meta=np.ndarray>\n",
-       "    tswfluxdn         (time, lat, lon) float32 dask.array<chunksize=(2, 15, 17), meta=np.ndarray>\n",
-       "    ...                ...\n",
-       "    cf_clrfluxdn      (time) int32 dask.array<chunksize=(2,), meta=np.ndarray>\n",
-       "    cdirfluxdn        (time, lat, lon) float32 dask.array<chunksize=(2, 15, 17), meta=np.ndarray>\n",
-       "    qc_cdirfluxdn     (time, lat, lon) int32 dask.array<chunksize=(2, 15, 17), meta=np.ndarray>\n",
-       "    cf_cdirfluxdn     (time) int32 dask.array<chunksize=(2,), meta=np.ndarray>\n",
-       "    azimuth           (time) float32 dask.array<chunksize=(2,), meta=np.ndarray>\n",
-       "    alt               (time) float32 318.0 318.0 318.0 ... 318.0 318.0 318.0\n",
-       "Attributes: (12/18)\n",
-       "    Date:                           Wed Jun 16 21:58:59 2010\n",
-       "    Version:                        $State: process-vap-sfccldgrid1long-2.0-0 $\n",
-       "    Command_Line:                   sfccldgrid1long -d 20091123\n",
-       "    Input_Platforms:                sgp15swfanalbrs1longC1.c1, sgp15swfanalsi...\n",
-       "    BW_Version:                     $State: ds-dsutil-bw-4.3-0 $\n",
-       "    qc_format_version:              0.1\n",
-       "    ...                             ...\n",
-       "    history:                        created by user dsmgr on machine zinc at ...\n",
-       "    _file_dates:                    ['20091123', '20091124', '20091125']\n",
-       "    _file_times:                    ['180000', '144500', '144500']\n",
-       "    datastream:                     sgp15swfcldgrid1longN1.c1\n",
-       "    _datastream:                    sgp15swfcldgrid1longN1.c1\n",
-       "    _arm_standards_flag:            1
" - ], - "text/plain": [ - "\n", - "Dimensions: (time: 33, lat: 15, lon: 17)\n", - "Coordinates:\n", - " * time (time) datetime64[ns] 2009-11-23T18:00:00 ... 2009-11-2...\n", - " * lat (lat) float32 38.5 38.25 38.0 37.75 ... 35.5 35.25 35.0\n", - " * lon (lon) float32 99.5 99.25 99.0 98.75 ... 96.0 95.75 95.5\n", - "Data variables: (12/22)\n", - " base_time (time) datetime64[ns] 2009-11-23T18:00:00 ... 2009-11-2...\n", - " time_offset (time) datetime64[ns] 2009-11-23T18:00:00 ... 2009-11-2...\n", - " cloudfraction (time, lat, lon) float32 dask.array\n", - " qc_cloudfraction (time, lat, lon) int32 dask.array\n", - " cf_cloudfraction (time) int32 dask.array\n", - " tswfluxdn (time, lat, lon) float32 dask.array\n", - " ... ...\n", - " cf_clrfluxdn (time) int32 dask.array\n", - " cdirfluxdn (time, lat, lon) float32 dask.array\n", - " qc_cdirfluxdn (time, lat, lon) int32 dask.array\n", - " cf_cdirfluxdn (time) int32 dask.array\n", - " azimuth (time) float32 dask.array\n", - " alt (time) float32 318.0 318.0 318.0 ... 318.0 318.0 318.0\n", - "Attributes: (12/18)\n", - " Date: Wed Jun 16 21:58:59 2010\n", - " Version: $State: process-vap-sfccldgrid1long-2.0-0 $\n", - " Command_Line: sfccldgrid1long -d 20091123\n", - " Input_Platforms: sgp15swfanalbrs1longC1.c1, sgp15swfanalsi...\n", - " BW_Version: $State: ds-dsutil-bw-4.3-0 $\n", - " qc_format_version: 0.1\n", - " ... ...\n", - " history: created by user dsmgr on machine zinc at ...\n", - " _file_dates: ['20091123', '20091124', '20091125']\n", - " _file_times: ['180000', '144500', '144500']\n", - " datastream: sgp15swfcldgrid1longN1.c1\n", - " _datastream: sgp15swfcldgrid1longN1.c1\n", - " _arm_standards_flag: 1" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Load files as a single dataset\n", - "files_list = files_filter \n", - "ds = act.io.armfiles.read_netcdf(files_list)\n", - "ds.clean.cleanup()\n", - "print(f'{len(files_list)} files loaded')\n", - "ds\n" - ] - }, - { - "cell_type": "markdown", - "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", - "metadata": {}, - "source": [ - "## Plot time series data\n", - "#### Define the list of variables to be plotted:" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "id": "d09b789e-84f1-4605-846b-a72c110c8048", - "metadata": {}, - "outputs": [], - "source": [ - "variables_to_plot = ['cloudfraction', 'cf_cloudfraction', 'tswfluxdn']" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", - "metadata": {}, - "outputs": [ - { - "ename": "TypeError", - "evalue": "Dimensions of C (17, 15, 33) should be one smaller than X(33) and Y(15) while using shading='flat' see help(pcolormesh)", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", - "Cell \u001b[0;32mIn[9], line 5\u001b[0m\n\u001b[1;32m 2\u001b[0m ts_display\u001b[38;5;241m.\u001b[39madd_subplots((\u001b[38;5;28mlen\u001b[39m(variables_to_plot),), figsize \u001b[38;5;241m=\u001b[39m (\u001b[38;5;241m9.5\u001b[39m,\u001b[38;5;241m4\u001b[39m\u001b[38;5;241m*\u001b[39m\u001b[38;5;28mlen\u001b[39m(variables_to_plot)))\n\u001b[1;32m 4\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m i,v \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28menumerate\u001b[39m(variables_to_plot):\n\u001b[0;32m----> 5\u001b[0m ts_ax \u001b[38;5;241m=\u001b[39m \u001b[43mts_display\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mplot\u001b[49m\u001b[43m(\u001b[49m\u001b[43mv\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43msubplot_index\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mi\u001b[49m\u001b[43m,\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mset_title\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mds\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mvariables\u001b[49m\u001b[43m[\u001b[49m\u001b[43mv\u001b[49m\u001b[43m]\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mattrs\u001b[49m\u001b[43m[\u001b[49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43mlong_name\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m,\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 6\u001b[0m ts_ax\u001b[38;5;241m.\u001b[39mgrid()\n\u001b[1;32m 8\u001b[0m plt\u001b[38;5;241m.\u001b[39mshow()\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/act/plotting/timeseriesdisplay.py:588\u001b[0m, in \u001b[0;36mTimeSeriesDisplay.plot\u001b[0;34m(self, field, dsname, subplot_index, cmap, set_title, add_nan, day_night_background, invert_y_axis, abs_limits, time_rng, y_rng, use_var_for_y, set_shading, assessment_overplot, overplot_marker, overplot_behind, overplot_markersize, assessment_overplot_category, assessment_overplot_category_color, force_line_plot, labels, cbar_label, cbar_h_adjust, secondary_y, y_axis_flag_meanings, colorbar_labels, cb_friendly, **kwargs)\u001b[0m\n\u001b[1;32m 586\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124medgecolors\u001b[39m\u001b[38;5;124m'\u001b[39m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;129;01min\u001b[39;00m kwargs\u001b[38;5;241m.\u001b[39mkeys():\n\u001b[1;32m 587\u001b[0m kwargs[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124medgecolors\u001b[39m\u001b[38;5;124m'\u001b[39m] \u001b[38;5;241m=\u001b[39m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mface\u001b[39m\u001b[38;5;124m'\u001b[39m\n\u001b[0;32m--> 588\u001b[0m mesh \u001b[38;5;241m=\u001b[39m \u001b[43max\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mpcolormesh\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 589\u001b[0m \u001b[43m \u001b[49m\u001b[43mnp\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43masarray\u001b[49m\u001b[43m(\u001b[49m\u001b[43mxdata\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 590\u001b[0m \u001b[43m \u001b[49m\u001b[43mydata\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 591\u001b[0m \u001b[43m \u001b[49m\u001b[43mdata\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mtranspose\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 592\u001b[0m \u001b[43m \u001b[49m\u001b[43mshading\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mset_shading\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 593\u001b[0m \u001b[43m \u001b[49m\u001b[43mcmap\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcmap\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 594\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 595\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 597\u001b[0m \u001b[38;5;66;03m# Set Title\u001b[39;00m\n\u001b[1;32m 598\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m set_title \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/matplotlib/__init__.py:1442\u001b[0m, in \u001b[0;36m_preprocess_data..inner\u001b[0;34m(ax, data, *args, **kwargs)\u001b[0m\n\u001b[1;32m 1439\u001b[0m \u001b[38;5;129m@functools\u001b[39m\u001b[38;5;241m.\u001b[39mwraps(func)\n\u001b[1;32m 1440\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21minner\u001b[39m(ax, \u001b[38;5;241m*\u001b[39margs, data\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mNone\u001b[39;00m, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs):\n\u001b[1;32m 1441\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m data \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[0;32m-> 1442\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mfunc\u001b[49m\u001b[43m(\u001b[49m\u001b[43max\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;28;43mmap\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43msanitize_sequence\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43margs\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 1444\u001b[0m bound \u001b[38;5;241m=\u001b[39m new_sig\u001b[38;5;241m.\u001b[39mbind(ax, \u001b[38;5;241m*\u001b[39margs, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs)\n\u001b[1;32m 1445\u001b[0m auto_label \u001b[38;5;241m=\u001b[39m (bound\u001b[38;5;241m.\u001b[39marguments\u001b[38;5;241m.\u001b[39mget(label_namer)\n\u001b[1;32m 1446\u001b[0m \u001b[38;5;129;01mor\u001b[39;00m bound\u001b[38;5;241m.\u001b[39mkwargs\u001b[38;5;241m.\u001b[39mget(label_namer))\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/matplotlib/axes/_axes.py:6220\u001b[0m, in \u001b[0;36mAxes.pcolormesh\u001b[0;34m(self, alpha, norm, cmap, vmin, vmax, shading, antialiased, *args, **kwargs)\u001b[0m\n\u001b[1;32m 6217\u001b[0m shading \u001b[38;5;241m=\u001b[39m shading\u001b[38;5;241m.\u001b[39mlower()\n\u001b[1;32m 6218\u001b[0m kwargs\u001b[38;5;241m.\u001b[39msetdefault(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124medgecolors\u001b[39m\u001b[38;5;124m'\u001b[39m, \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mnone\u001b[39m\u001b[38;5;124m'\u001b[39m)\n\u001b[0;32m-> 6220\u001b[0m X, Y, C, shading \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_pcolorargs\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43mpcolormesh\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 6221\u001b[0m \u001b[43m \u001b[49m\u001b[43mshading\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mshading\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mkwargs\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 6222\u001b[0m coords \u001b[38;5;241m=\u001b[39m np\u001b[38;5;241m.\u001b[39mstack([X, Y], axis\u001b[38;5;241m=\u001b[39m\u001b[38;5;241m-\u001b[39m\u001b[38;5;241m1\u001b[39m)\n\u001b[1;32m 6223\u001b[0m \u001b[38;5;66;03m# convert to one dimensional array, except for 3D RGB(A) arrays\u001b[39;00m\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/matplotlib/axes/_axes.py:5751\u001b[0m, in \u001b[0;36mAxes._pcolorargs\u001b[0;34m(self, funcname, shading, *args, **kwargs)\u001b[0m\n\u001b[1;32m 5749\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m shading \u001b[38;5;241m==\u001b[39m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mflat\u001b[39m\u001b[38;5;124m'\u001b[39m:\n\u001b[1;32m 5750\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m (Nx, Ny) \u001b[38;5;241m!=\u001b[39m (ncols \u001b[38;5;241m+\u001b[39m \u001b[38;5;241m1\u001b[39m, nrows \u001b[38;5;241m+\u001b[39m \u001b[38;5;241m1\u001b[39m):\n\u001b[0;32m-> 5751\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mTypeError\u001b[39;00m(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mDimensions of C \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mC\u001b[38;5;241m.\u001b[39mshape\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m should\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 5752\u001b[0m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m be one smaller than X(\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mNx\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m) and Y(\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mNy\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m)\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 5753\u001b[0m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m while using shading=\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mflat\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 5754\u001b[0m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m see help(\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mfuncname\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m)\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[1;32m 5755\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m: \u001b[38;5;66;03m# ['nearest', 'gouraud']:\u001b[39;00m\n\u001b[1;32m 5756\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m (Nx, Ny) \u001b[38;5;241m!=\u001b[39m (ncols, nrows):\n", - "\u001b[0;31mTypeError\u001b[0m: Dimensions of C (17, 15, 33) should be one smaller than X(33) and Y(15) while using shading='flat' see help(pcolormesh)" - ] - }, - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "5ec59307f5704277961c72d61c88ebc6", - "version_major": 2, - "version_minor": 0 - }, - "image/png": "iVBORw0KGgoAAAANSUhEUgAAA7YAAASwCAYAAADPBNYLAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAA9hAAAPYQGoP6dpAABi0UlEQVR4nO3df3RV5Z3o/08gkKA1sUAJIIjRaqXlqkO4IrF8Ha3GQceWGeeKY5eog/c2Vy0DqV5FZvmD5axMO6tO6w9Qr6DjXWgz/hxmbkbNzFhFwRlJg+MIrb1CDWgiTRwT1DYI7O8fLjJNE5Rf5yQPvF5rnT/Ow7OTZ283cb/Z5+QUZFmWBQAAACRqUH8vAAAAAPaHsAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAnbHHvhhRfiggsuiLFjx0ZBQUE89dRTn7nN888/HxUVFVFcXBzHHnts3HPPPblfKAAAQKKEbY59+OGHcfLJJ8ddd921R/M3btwY5513XkyfPj2amprixhtvjLlz58bjjz+e45UCAACkqSDLsqy/F3GoKCgoiCeffDJmzpy52znXX399rFixItavX989Vl1dHa+++mqsXr06D6sEAABIS2F/L4CeVq9eHVVVVT3Gzj333Fi6dGl8/PHHMWTIkD636+rqiq6uru7nO3fujPfeey9GjBgRBQUFOV0zAAAc6rIsi61bt8bYsWNj0CAvjM03YTvAtLa2RllZWY+xsrKy2L59e7S1tcWYMWP63K62tjZuvfXWfCwRAADYjU2bNsW4ceP6exmHHGE7AP32HdZdrxb/tDuvCxYsiJqamu7nHR0dcfTRR8emTZuipKQkNwsFAAAiIqKzszPGjx8fRxxxRH8v5ZAkbAeY0aNHR2tra4+xLVu2RGFhYYwYMWK32xUVFUVRUVGv8ZKSEmELAAB54m2A/cOLvweYadOmRUNDQ4+xZ599NqZMmbLb99cCAAAcyoRtjn3wwQexdu3aWLt2bUR88nE+a9eujebm5oj45CXEs2fP7p5fXV0db731VtTU1MT69etj2bJlsXTp0rj22mv7Y/kAAAADnpci59iaNWvizDPP7H6+632wl112WTz44IPR0tLSHbkREeXl5VFfXx/z58+Pu+++O8aOHRt33HFHXHjhhXlfOwAAQAp8ju1BqrOzM0pLS6Ojo8N7bAEAIMdcf/cvL0UGAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbPNk8eLFUV5eHsXFxVFRURErV6781PnLly+Pk08+OQ477LAYM2ZMXHHFFdHe3p6n1QIAAKRD2OZBXV1dzJs3LxYuXBhNTU0xffr0mDFjRjQ3N/c5/8UXX4zZs2fHnDlz4vXXX49HH300XnnllbjyyivzvHIAAICBT9jmwe233x5z5syJK6+8MiZOnBg/+MEPYvz48bFkyZI+57/88stxzDHHxNy5c6O8vDy++tWvxre+9a1Ys2ZNnlcOAAAw8AnbHNu2bVs0NjZGVVVVj/GqqqpYtWpVn9tUVlbG5s2bo76+PrIsi3fffTcee+yxOP/883f7fbq6uqKzs7PHAwAA4FAgbHOsra0tduzYEWVlZT3Gy8rKorW1tc9tKisrY/ny5TFr1qwYOnRojB49Oo488si48847d/t9amtro7S0tPsxfvz4A7ofAAAAA5WwzZOCgoIez7Ms6zW2y7p162Lu3Llx0003RWNjYzz99NOxcePGqK6u3u3XX7BgQXR0dHQ/Nm3adEDXDwAAMFAV9vcCDnYjR46MwYMH97o7u2XLll53cXepra2N008/Pa677rqIiDjppJPi8MMPj+nTp8dtt90WY8aM6bVNUVFRFBUVHfgdAAAAGODcsc2xoUOHRkVFRTQ0NPQYb2hoiMrKyj63+eijj2LQoJ7/aQYPHhwRn9zpBQAA4D8J2zyoqamJ+++/P5YtWxbr16+P+fPnR3Nzc/dLixcsWBCzZ8/unn/BBRfEE088EUuWLIkNGzbESy+9FHPnzo1TTz01xo4d21+7AQAAMCB5KXIezJo1K9rb22PRokXR0tISkyZNivr6+pgwYUJERLS0tPT4TNvLL788tm7dGnfddVd85zvfiSOPPDLOOuus+O53v9tfuwAAADBgFWRe23pQ6uzsjNLS0ujo6IiSkpL+Xg4AABzUXH/3Ly9FBgAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmzzZPHixVFeXh7FxcVRUVERK1eu/NT5XV1dsXDhwpgwYUIUFRXFcccdF8uWLcvTagEAANJR2N8LOBTU1dXFvHnzYvHixXH66afHvffeGzNmzIh169bF0Ucf3ec2F110Ubz77ruxdOnS+OIXvxhbtmyJ7du353nlAAAAA19BlmVZfy/iYDd16tSYPHlyLFmypHts4sSJMXPmzKitre01/+mnn46LL744NmzYEMOHD9+n79nZ2RmlpaXR0dERJSUl+7x2AADgs7n+7l9eipxj27Zti8bGxqiqquoxXlVVFatWrepzmxUrVsSUKVPie9/7Xhx11FFxwgknxLXXXhu/+tWv8rFkAACApHgpco61tbXFjh07oqysrMd4WVlZtLa29rnNhg0b4sUXX4zi4uJ48skno62tLa666qp47733dvs+266urujq6up+3tnZeeB2AgAAYABzxzZPCgoKejzPsqzX2C47d+6MgoKCWL58eZx66qlx3nnnxe233x4PPvjgbu/a1tbWRmlpafdj/PjxB3wfAAAABiJhm2MjR46MwYMH97o7u2XLll53cXcZM2ZMHHXUUVFaWto9NnHixMiyLDZv3tznNgsWLIiOjo7ux6ZNmw7cTgAAAAxgwjbHhg4dGhUVFdHQ0NBjvKGhISorK/vc5vTTT4933nknPvjgg+6xN954IwYNGhTjxo3rc5uioqIoKSnp8QAAADgUCNs8qKmpifvvvz+WLVsW69evj/nz50dzc3NUV1dHxCd3W2fPnt09/5JLLokRI0bEFVdcEevWrYsXXnghrrvuuviTP/mTGDZsWH/tBgAAwIDkl0fl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pEmTor6+PiZMmBARES0tLT0+0/bee++N7du3x9VXXx1XX3119/hll10WDz74YL6XDwAAMKD5HNuDlM/RAgCA/HH93b+8xxYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwzZPFixdHeXl5FBcXR0VFRaxcufJT5z///PNRUVERxcXFceyxx8Y999yTp5UCAACkRdjmQV1dXcybNy8WLlwYTU1NMX369JgxY0Y0Nzf3OX/jxo1x3nnnxfTp06OpqSluvPHGmDt3bjz++ON5XjkAAMDAV5BlWdbfizjYTZ06NSZPnhxLlizpHps4cWLMnDkzamtre82//vrrY8WKFbF+/fruserq6nj11Vdj9erVe/Q9Ozs7o7S0NDo6OqKkpGT/dwIAANgt19/9yx3bHNu2bVs0NjZGVVVVj/GqqqpYtWpVn9usXr261/xzzz031qxZEx9//HHO1goAAJCiwv5ewMGura0tduzYEWVlZT3Gy8rKorW1tc9tWltb+5y/ffv2aGtrizFjxvTapqurK7q6urqfd3R0RMQn/3IEAADk1q7rbi+I7R/CNk8KCgp6PM+yrNfYZ83va3yX2trauPXWW3uNjx8/fm+XCgAA7KP29vYoLS3t72UccoRtjo0cOTIGDx7c6+7sli1bet2V3WX06NF9zi8sLIwRI0b0uc2CBQuipqam+/n7778fEyZMiObmZn+xPkVnZ2eMHz8+Nm3a5L0Qu+EY7RnH6bM5RnvGcdozjtNnc4z2jOP02RyjPdPR0RFHH310DB8+vL+XckgStjk2dOjQqKioiIaGhviDP/iD7vGGhob4xje+0ec206ZNi7/7u7/rMfbss8/GlClTYsiQIX1uU1RUFEVFRb3GS0tL/QDaAyUlJY7TZ3CM9ozj9Nkcoz3jOO0Zx+mzOUZ7xnH6bI7Rnhk0yK8x6g+Oeh7U1NTE/fffH8uWLYv169fH/Pnzo7m5OaqrqyPik7uts2fP7p5fXV0db731VtTU1MT69etj2bJlsXTp0rj22mv7axcAAAAGLHds82DWrFnR3t4eixYtipaWlpg0aVLU19fHhAkTIiKipaWlx2falpeXR319fcyfPz/uvvvuGDt2bNxxxx1x4YUX9tcuAAAADFjCNk+uuuqquOqqq/r8swcffLDX2BlnnBE/+clP9vn7FRUVxc0339zny5P5T47TZ3OM9ozj9Nkcoz3jOO0Zx+mzOUZ7xnH6bI7RnnGc+ldB5vdRAwAAkDDvsQUAACBpwhYAAICkCVsAAACSJmwHqNra2viv//W/xhFHHBGjRo2KmTNnxs9+9rMec7Isi1tuuSXGjh0bw4YNi9/93d+N119/vcecrq6u+Pa3vx0jR46Mww8/PL7+9a/H5s2be8z5j//4j7j00kujtLQ0SktL49JLL433338/17t4QOTzOP35n/95VFZWxmGHHRZHHnlkrnftgMnXMfrFL34Rc+bMifLy8hg2bFgcd9xxcfPNN8e2bdvysp/7K5/n0te//vU4+uijo7i4OMaMGROXXnppvPPOOznfx/2Vz2P0m3NPOeWUKCgoiLVr1+Zq1w6ofB6nY445JgoKCno8brjhhpzv44GQ7/Pp//7f/xtTp06NYcOGxciRI+MP//APc7p/B0K+jtGPf/zjXufRrscrr7ySl33dH/k8l9544434xje+ESNHjoySkpI4/fTT47nnnsv5Ph4I+TxOP/nJT+Kcc86JI488MkaMGBH/43/8j/jggw9yvo/760Ado/vuuy9+93d/N0pKSqKgoKDP6+qUr78HrIwB6dxzz80eeOCB7N///d+ztWvXZueff3529NFHZx988EH3nL/4i7/IjjjiiOzxxx/PXnvttWzWrFnZmDFjss7Ozu451dXV2VFHHZU1NDRkP/nJT7IzzzwzO/nkk7Pt27d3z/m93/u9bNKkSdmqVauyVatWZZMmTcp+//d/P6/7u6/yeZxuuumm7Pbbb89qamqy0tLSfO7mfsnXMfqHf/iH7PLLL8+eeeaZ7M0338z+9m//Nhs1alT2ne98J+/7vC/yeS7dfvvt2erVq7Nf/OIX2UsvvZRNmzYtmzZtWl73d1/k8xjtMnfu3GzGjBlZRGRNTU352M39ls/jNGHChGzRokVZS0tL92Pr1q153d99lc/j9Nhjj2Wf//znsyVLlmQ/+9nPsp/+9KfZo48+mtf93Rf5OkZdXV09zqGWlpbsyiuvzI455phs586ded/vvZXPc+mLX/xidt5552Wvvvpq9sYbb2RXXXVVdthhh2UtLS153ed9ka/j9Pbbb2ef//zns+rq6uynP/1p9q//+q9ZZWVlduGFF+Z9n/fWgTpGf/VXf5XV1tZmtbW1WURk//Ef/9Hre6V8/T1QCdtEbNmyJYuI7Pnnn8+yLMt27tyZjR49OvuLv/iL7jm//vWvs9LS0uyee+7JsizL3n///WzIkCHZj370o+45b7/9djZo0KDs6aefzrIsy9atW5dFRPbyyy93z1m9enUWEdlPf/rTfOzaAZWr4/SbHnjggaTC9rfl4xjt8r3vfS8rLy/P0Z7kVj6P09/+7d9mBQUF2bZt23K0N7mR62NUX1+fnXjiidnrr7+eVNj+tlwepwkTJmR/9Vd/lZ8dybFcHaePP/44O+qoo7L7778/j3uTG/n6ubRt27Zs1KhR2aJFi3K4N7mTq+P0y1/+MouI7IUXXuie09nZmUVE9o//+I/52LUDKlfH6d57781GjRqV7dixo3tOU1NTFhHZz3/+83zs2gGzL8foNz333HN9hu3Bdv09UHgpciI6OjoiImL48OEREbFx48ZobW2Nqqqq7jlFRUVxxhlnxKpVqyIiorGxMT7++OMec8aOHRuTJk3qnrN69eooLS2NqVOnds857bTTorS0tHtOSnJ1nA4m+TxGHR0d3d8nNfk6Tu+9914sX748KisrY8iQIbnanZzI5TF6991347//9/8e/+f//J847LDD8rE7OZPrc+m73/1ujBgxIk455ZT48z//82Re/v/bcnWcfvKTn8Tbb78dgwYNit/5nd+JMWPGxIwZM3q9dDAF+fq5tGLFimhra4vLL788R3uSW7k6TiNGjIiJEyfGQw89FB9++GFs37497r333igrK4uKiop87d4Bk6vj1NXVFUOHDo1Bg/4zM4YNGxYRES+++GJud+oA25djtCcOtuvvgULYJiDLsqipqYmvfvWrMWnSpIiIaG1tjYiIsrKyHnPLysq6/6y1tTWGDh0an//85z91zqhRo3p9z1GjRnXPSUUuj9PBIp/H6M0334w777wzqqurD/Ru5Fw+jtP1118fhx9+eIwYMSKam5vjb//2b3O1OzmRy2OUZVlcfvnlUV1dHVOmTMn1ruRUrs+lP/3TP40f/ehH8dxzz8U111wTP/jBD+Kqq67K5S7lRC6P04YNGyIi4pZbbok/+7M/i7//+7+Pz3/+83HGGWfEe++9l9P9OpDy+fN76dKlce6558b48eMP9G7kXC6PU0FBQTQ0NERTU1McccQRUVxcHH/1V38VTz/9dFK/eyMit8fprLPOitbW1vjLv/zL2LZtW/zHf/xH3HjjjRER0dLSktP9OpD29RjtiYPp+nsgEbYJuOaaa+Lf/u3f4pFHHun1ZwUFBT2eZ1nWa+y3/facvubvydcZaHJ9nA4G+TpG77zzTvze7/1e/Lf/9t/iyiuv3L9F94N8HKfrrrsumpqa4tlnn43BgwfH7NmzI8uy/V98nuTyGN15553R2dkZCxYsOHAL7ie5Ppfmz58fZ5xxRpx00klx5ZVXxj333BNLly6N9vb2A7MDeZLL47Rz586IiFi4cGFceOGFUVFREQ888EAUFBTEo48+eoD2IPfy9fN78+bN8cwzz8ScOXP2b8H9JJfHKcuyuOqqq2LUqFGxcuXK+Nd//df4xje+Eb//+7+fVLBF5PY4feUrX4m//uu/ju9///tx2GGHxejRo+PYY4+NsrKyGDx48IHbiRw70Mfos77Gvn4d/pOwHeC+/e1vx4oVK+K5556LcePGdY+PHj06IqLXv+ps2bKl+1+RRo8e3f0vZZ8259133+31fX/5y1/2+teogSzXx+lgkK9j9M4778SZZ54Z06ZNi/vuuy8Xu5JT+TpOI0eOjBNOOCHOOeec+NGPfhT19fXx8ssv52KXDrhcH6N//ud/jpdffjmKioqisLAwvvjFL0ZExJQpU+Kyyy7L2X4daP3xc+m0006LiIj/9//+3wHZh3zI9XEaM2ZMRER8+ctf7v7zoqKiOPbYY6O5ufnA71AO5PNceuCBB2LEiBHx9a9//UDvRs7l42fT3//938ePfvSjOP3002Py5MmxePHiGDZsWPz1X/91LnftgMrH+XTJJZdEa2trvP3229He3h633HJL/PKXv4zy8vJc7dYBtT/HaE8cLNffA05O38HLPtu5c2d29dVXZ2PHjs3eeOONPv989OjR2Xe/+93usa6urj7f4F9XV9c955133unzl0f9y7/8S/ecl19+OZk3r+frOP2m1H55VD6P0ebNm7Pjjz8+u/jii/v8DbcDWX+cS7s0NzdnEZE999xzB26HciBfx+itt97KXnvtte7HM888k0VE9thjj2WbNm3K8V7uv/48l/7u7/4ui4jsrbfeOoB7lBv5Ok4dHR1ZUVFRj18eteuXI91777252r0DIt/n0s6dO7Py8vJkfpv9Lvk6TitWrMgGDRrU6zePn3DCCdmf//mf52LXDqj+/Nm0dOnS7LDDDuvztwMPJAfiGP2mz/rlUalefw9UwnaA+p//839mpaWl2Y9//OMev37/o48+6p7zF3/xF1lpaWn2xBNPZK+99lr2x3/8x33+SvZx48Zl//iP/5j95Cc/yc4666w+P+7npJNOylavXp2tXr06+y//5b8k8+vG83mc3nrrraypqSm79dZbs8997nNZU1NT1tTUNOA/WiNfx+jtt9/OvvjFL2ZnnXVWtnnz5h7fKwX5Ok7/8i//kt15551ZU1NT9otf/CL753/+5+yrX/1qdtxxx2W//vWv877feyOff99+08aNG5P6rcj5Ok6rVq3Kbr/99qypqSnbsGFDVldXl40dOzb7+te/nvd93hf5PJ/+9E//NDvqqKOyZ555JvvpT3+azZkzJxs1alT23nvv5XWf91a+/8794z/+YxYR2bp16/K2jwdCvo7TL3/5y2zEiBHZH/7hH2Zr167Nfvazn2XXXnttNmTIkGzt2rV53++9lc/z6c4778waGxuzn/3sZ9ldd92VDRs2LPvhD3+Y1/3dFwfqGLW0tGRNTU3Z//7f/7v7N2k3NTVl7e3t3XNSvv4eqITtABURfT4eeOCB7jk7d+7Mbr755mz06NFZUVFR9v/9f/9f9tprr/X4Or/61a+ya665Jhs+fHg2bNiw7Pd///ez5ubmHnPa29uzb37zm9kRRxyRHXHEEdk3v/nNAf8varvk8zhddtllfX6vgX6XLV/H6IEHHtjt90pBvo7Tv/3bv2VnnnlmNnz48KyoqCg75phjsurq6mzz5s352tV9ls+/b78ptbDN13FqbGzMpk6dmpWWlmbFxcXZl770pezmm2/OPvzww3zt6n7J5/m0bdu27Dvf+U42atSo7IgjjsjOPvvs7N///d/zsZv7Jd9/5/74j/84q6yszPVu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9tlnY8qUKTFkyJA+tykqKoqioqJe46WlpX5Ic0CUlJQ4lzggnEscSM4nDhTnEgfKoEF+jVF/cNTzoKamJu6///5YtmxZrF+/PubPnx/Nzc1RXV0dEZ/cbZ09e3b3/Orq6njrrbeipqYm1q9fH8uWLYulS5fGtdde21+7AAAAMGC5Y5sHs2bNivb29li0aFG0tLTEpEmTor6+PiZMmBARES0tLT0+07a8vDzq6+tj/vz5cffdd8fYsWPjjjvuiAsvvLC/dgEAAGDAErZ5ctVVV8VVV13V5589+OCDvcbOOOOM+MlPfrLP36+oqChuvvnmPl+eDHvDucSB4lziQHI+caA4lzhQnEv9qyDz+6gBAABImPfYAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2CZs8eLFUV5eHsXFxVFRURErV6781PnPP/98VFRURHFxcRx77LFxzz335GmlDHR7cy498cQTcc4558QXvvCFKCkpiWnTpsUzzzyTx9UykO3tz6VdXnrppSgsLIxTTjkltwskGXt7LnV1dcXChQtjwoQJUVRUFMcdd1wsW7YsT6tloNvb82n58uVx8sknx2GHHRZjxoyJK664Itrb2/O0WgaqF154IS644IIYO3ZsFBQUxFNPPfWZ27j+zh9hm6i6urqYN29eLFy4MJqammL69OkxY8aMHp+H+5s2btwY5513XkyfPj2amprixhtvjLlz58bjjz+e55Uz0OztufTCCy/EOeecE/X19dHY2BhnnnlmXHDBBdHU1JTnlTPQ7O25tEtHR0fMnj07vva1r+VppQx0+3IuXXTRRfFP//RPsXTp0vjZz34WjzzySJx44ol5XDUD1d6eTy+++GLMnj075syZE6+//no8+uij8corr8SVV16Z55Uz0Hz44Ydx8sknx1133bVH811/51lGkk499dSsurq6x9iJJ56Y3XDDDX3O/1//639lJ554Yo+xb33rW9lpp52WszWShr09l/ry5S9/Obv11lsP9NJIzL6eS7Nmzcr+7M/+LLv55puzk08+OYcrJBV7ey79wz/8Q1ZaWpq1t7fnY3kkZm/Pp7/8y7/Mjj322B5jd9xxRzZu3LicrZH0RET25JNPfuoc19/55Y5tgrZt2xaNjY1RVVXVY7yqqipWrVrV5zarV6/uNf/cc8+NNWvWxMcff5yztTKw7cu59Nt27twZ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atSo3c6rra2N0tLS7sf48eP3e80AAAApELY5NnLkyBg8eHCvu7NbtmzpdRf3t9XV1cWcOXPib/7mb+Lss8/+1LkLFiyIjo6O7semTZv2e+0AAAApELY5NnTo0KioqIiGhoYe4w0NDVFZWbnb7R555JG4/PLL4+GHH47zzz//M79PUVFRlJSU9HgAAAAcCrzHNg9qamri0ksvjSlTpsS0adPivvvui+bm5qiuro6IT+62vv322/HQQw9FxCdRO3v27PjhD38Yp512Wvfd3mHDhkVpaWm/7QcAAMBAJGzzYNasWdHe3h6LFi2KlpaWmDRpUtTX18eECRMiIqKlpaXHZ9ree++9sX379rj66qvj6quv7h6/7LLL4sEHH8z38gEAAAY0n2N7kPI5WgAAkD+uv/uX99gCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2ObJ4sWLo7y8PIqLi6OioiJWrlz5qfOff/75qKioiOLi4jj22GPjnnvuydNKAQAA0iJs86Curi7mzZsXCxcujKamppg+fXrMmDEjmpub+5y/cePGOO+882L69OnR1NQUN954Y8ydOzcef/zxPK8cAABg4CvIsizr70Uc7KZOnRqTJ0+OJUuWdI9NnDgxZs6cGbW1tb3mX3/99bFixYpYv35991h1dXW8+uqrsXr16j36np2dnVFaWhodHR1RUlKy/zsBAADsluvv/lXY3ws42G3bti0aGxvjhhtu6DFeVVUVq1at6nOb1atXR1VVVY+xc889N5YuXRoff/xxDBkypNc2XV1d0dXV1f28o6MjIj75CwYAAOTWrutu9w37h7DNsba2ttixY0eUlZX1GC8rK4vW1tY+t2ltbe1z/vbt26OtrS3GjBnTa5va2tq49dZbe42PHz9+P1YPAADsjfb29igtLe3vZRxyhG2eFBQU9HieZVmvsc+a39f4LgsWLIiampru5++//35MmDAhmpub/cViv3R2dsb48eNj06ZNXlbDfnEucSA5nzhQnEscKB0dHXH00UfH8OHD+3sphyRhm2MjR46MwYMH97o7u2XLll53ZXcZPXp0n/MLCwtjxIgRfW5TVFQURUVFvcZLS0v9kOaAKCkpcS5xQDiXOJCcTxwoziUOlEGD/H7e/uCo59jQoUOjoqIiGhoaeow3NDREZWVln9tMmzat1/xnn302pkyZ0uf7awEAAA5lwjYPampq4v77749ly5bF+vXrY/78+dHc3BzV1dUR8cnLiGfPnt09v7q6Ot56662oqamJ9evXx7Jly2Lp0qVx7bXX9tcuAAAADFheipwHs2bNivb29li0aFG0tLTEpEmTor6+PiZMmBARES0tLT0+07a8vDzq6+tj/vz5cffdd8fYsWPjjjvuiAsvvHCPv2dRUVHcfPPNfb48GfaGc4kDxbnEgeR84kBxLnGgOJf6l8+xBQAAIGleigwAAEDShC0AAABJE7YAAAAkTdgCAACQNGGbsMWLF0d5eXkUFxdHRUVFrFy58lPnP//881FRURHFxcVx7LHHxj333JOnlTLQ7c259MQTT8Q555wTX/jCF6KkpCSmTZsWzzzzTB5Xy0C2tz+XdnnppZeisLAwTjnllNwukGTs7bnU1dUVCxcujAkTJkRRUVEcd9xxsWzZsjytloFub8+n5cuXx8knnxyHHXZYjBkzJq644opob2/P02oZqF544YW44IILYuzYsVFQUBBPPfXUZ27j+jt/hG2i6urqYt68ebFw4cJoamqK6dOnx4wZM3p8bNBv2rhxY5x33nkxffr0aGpqihtvvDHmzp0bjz/+eJ5XzkCzt+fSCy+8EOecc07U19dHY2NjnHnmmXHBBRdEU1NTnlfOQLO359IuHR0dMXv27Pja176Wp5Uy0O3LuXTRRRfFP/3TP8XSpUvjZz/7WTzyyCNx4okn5nHVDFR7ez69+OKLMXv27JgzZ068/vrr8eijj8Yrr7wSV155ZZ5XzkDz4Ycfxsknnxx33XXXHs13/Z1nGUk69dRTs+rq6h5jJ554YnbDDTf0Of9//a//lZ144ok9xr71rW9lp512Ws7WSBr29lzqy5e//OXs1ltvPdBLIzH7ei7NmjUr+7M/+7Ps5ptvzk4++eQcrpBU7O259A//8A9ZaWlp1t7eno/lkZi9PZ/+8i//Mjv22GN7jN1xxx3ZuHHjcrZG0hMR2ZNPPvmpc1x/55c7tgnatm1bNDY2RlVVVY/xqqqqWLVqVZ/brF69utf8c889N9asWRMff/xxztbKwLYv59Jv27lzZ2zdujWGDx+eiyWSiH09lx544IF488034+abb871EknEvpxLK1asiClTpsT3vve9OOqoo+KEE06Ia6+9Nn71q1/lY8kMYPtyPlVWVsbmzZujvr4+siyLd999Nx577LE4//zz87FkDiKuv/OrsL8XwN5ra2uLHTt2RFlZWY/xsrKyaG1t7XOb1tbWPudv37492traYsyYMTlbLwPXvpxLv+373/9+fPjhh3HRRRflYokkYl/OpZ///Odxww03xMqVK6Ow0P+O+MS+nEsbNmyIF198MYqLi+PJJ5+Mtra2uOqqq+K9997zPttD3L6cT5WVlbF8+fKYNWtW/PrXv47t27fH17/+9bjzzjvzsWQOIq6/88sd24QVFBT0eJ5lWa+xz5rf1ziHnr09l3Z55JFH4pZbbom6uroYNWpUrpZHQvb0XNqxY0dccsklceutt8YJJ5yQr+WRkL35ubRz584oKCiI5cuXx6mnnhrnnXde3H777fHggw+6a0tE7N35tG7dupg7d27cdNNN0djYGE8//XRs3Lgxqqur87FUDjKuv/PHP5EnaOTIkTF48OBe/9K4ZcuWXv8qtMvo0aP7nF9YWBgjRozI2VoZ2PblXNqlrq4u5syZE48++micffbZuVwmCdjbc2nr1q2xZs2aaGpqimuuuSYiPomTLMuisLAwnn322TjrrLPysnYGln35uTRmzJg46qijorS0tHts4sSJkWVZbN68OY4//vicrpmBa1/Op9ra2jj99NPjuuuui4iIk046KQ4//PCYPn163Hbbbe6yscdcf+eXO7YJGjp0aFRUVERDQ0OP8YaGhqisrOxzm2nTpvWa/+yzz8aUKVNiyJAhOVsrA9u+nEsRn9ypvfzyy+Phhx/2niMiYu/PpZKSknjttddi7dq13Y/q6ur40pe+FGvXro2pU6fma+kMMPvyc+n000+Pd955Jz744IPusTfeeCMGDRoU48aNy+l6Gdj25Xz66KOPYtCgnpfIgwcPjoj/vNsGe8L1d5710y+tYj/96Ec/yoYMGZItXbo0W7duXTZv3rzs8MMPz37xi19kWZZlN9xwQ3bppZd2z9+wYUN22GGHZfPnz8/WrVuXLV26NBsyZEj22GOP9dcuMEDs7bn08MMPZ4WFhdndd9+dtbS0dD/ef//9/toFBoi9PZd+m9+KzC57ey5t3bo1GzduXPZHf/RH2euvv549//zz2fHHH59deeWV/bULDCB7ez498MADWWFhYbZ48eLszTffzF588cVsypQp2amnntpfu8AAsXXr1qypqSlramrKIiK7/fbbs6ampuytt97Kssz1d38Ttgm7++67swkTJmRDhw7NJk+enD3//PPdf3bZZZdlZ5xxRo/5P/7xj7Pf+Z3fyYYOHZodc8wx2ZIlS/K8YgaqvTmXzjjjjCwiej0uu+yy/C+cAWdvfy79JmHLb9rbc2n9+vXZ2WefnQ0bNiwbN25cVlNTk3300Ud5XjUD1d6eT3fccUf25S9/ORs2bFg2ZsyY7Jvf/Ga2efPmPK+agea555771Gsg19/9qyDLvKYCAACAdHmPLQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACTt/wcLAF7/XlacegAAAABJRU5ErkJggg==", - "text/html": [ - "\n", - "
\n", - "
\n", - " Figure\n", - "
\n", - " \n", - "
\n", - " " - ], - "text/plain": [ - "Canvas(toolbar=Toolbar(toolitems=[('Home', 'Reset original view', 'home', 'home'), ('Back', 'Back to previous …" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", - "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", - "\n", - "for i,v in enumerate(variables_to_plot):\n", - " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", - " ts_ax.grid()\n", - "\n", - "plt.show()\n" - ] - }, - { - "cell_type": "markdown", - "id": "194399aa-1907-452b-8ba9-bc31d7f60291", - "metadata": {}, - "source": [ - "## Quality check plots\n", - "#### Define variable for QC plot" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", - "metadata": {}, - "outputs": [], - "source": [ - "qc_variable = 'cloudfraction'" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", - "metadata": {}, - "outputs": [], - "source": [ - "# QC Plot\n", - "if ('qc_'+qc_variable) in ds.variables:\n", - "\n", - " # Plot\n", - " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", - " qc_display.add_subplots((2,), figsize = (9.5,10))\n", - " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", - " qc_ax.grid()\n", - " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", - "\n", - " plt.show()\n", - "else:\n", - " print(f'QC not available for the selected field: {qc_variable}')\n" - ] - }, - { - "cell_type": "markdown", - "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", - "metadata": {}, - "source": [ - "## Field selection dropdown menu\n", - "Select variable to be plotted from a dropdown menu" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", - "metadata": {}, - "outputs": [], - "source": [ - "plt.ioff()\n", - "\n", - "# populate dropdown menu with available variables \n", - "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", - "d_variable = 'cloudfraction'\n", - "dropdown = widgets.Dropdown(\n", - " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", - " value= d_variable,\n", - " description='Field:',\n", - " disabled=False,\n", - ")\n", - "dropdown.layout.margin = '0px 30% 0px 20%'\n", - "dropdown.layout.width = '50%'\n", - "\n", - "# set up display\n", - "i_display = act.plotting.TimeSeriesDisplay(ds)\n", - "i_display.add_subplots((1,), figsize = (9.5,5))\n", - "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", - "i_ax.grid()\n", - "i_fig = i_display.fig\n", - "\n", - "# update plot callback function\n", - "def update_plot(change):\n", - " i_ax.cla()\n", - " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", - " i_ax_new.grid()\n", - " i_fig.canvas.draw()\n", - " i_fig.canvas.flush_events()\n", - "\n", - "dropdown.observe(update_plot, names='value')\n", - "\n", - "widgets.AppLayout(\n", - " header=dropdown,\n", - " center=i_fig.canvas,\n", - " pane_heights=[1, 6,1]\n", - ")\n" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.16" - }, - "widgets": { - "application/vnd.jupyter.widget-state+json": { - "state": {}, - "version_major": 2, - "version_minor": 0 - } - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/VAPs/quicklook/SFCCLDGRID/.ipynb_checkpoints/sfccldgrid2longcaracena.c1-checkpoint.ipynb b/VAPs/quicklook/SFCCLDGRID/.ipynb_checkpoints/sfccldgrid2longcaracena.c1-checkpoint.ipynb deleted file mode 100644 index d7d3c877..00000000 --- a/VAPs/quicklook/SFCCLDGRID/.ipynb_checkpoints/sfccldgrid2longcaracena.c1-checkpoint.ipynb +++ /dev/null @@ -1,5150 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "70840257-70e4-45e2-b491-14bff5a257a3", - "metadata": {}, - "source": [ - "# SFCCLDGRID2LONGCARACENA.C1 Plots\n", - "\n", - "[Click here](https://www.arm.gov/capabilities/vaps/sfccldgrid) for more information about this vap." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "460fd89f-e034-452c-b837-f65c5958264f", - "metadata": {}, - "outputs": [], - "source": [ - "%matplotlib widget\n", - "import ipywidgets as widgets\n", - "\n", - "import matplotlib.pyplot as plt\n", - "import ipywidgets as widgets\n", - "import numpy as np\n", - "import pandas as pd\n", - "import os\n", - "from datetime import datetime\n", - "\n", - "import act\n", - "import xarray as xr\n", - "\n", - "# Data archive directory\n", - "DATA_DIR = r'/data/archive/'\n", - "\n", - "# Datastream info\n", - "DATASTREAM_NAME = 'sfccldgrid2longcaracena'\n", - "DATA_LEVEL = 'c1'\n", - "LOCATIONS = [{'end_date': '2020-06-01', 'facility': 'N1', 'site': 'sgp', 'start_date': '2011-10-21'}]" - ] - }, - { - "cell_type": "markdown", - "id": "9faaf875", - "metadata": {}, - "source": [ - "## Define site, facility, and date range" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "ac6764f5", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "The following locations and date ranges are available for this VAP:\n" - ] - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
sitefacilitystart_dateend_date
0sgpN12011-10-212020-06-01
\n", - "
" - ], - "text/plain": [ - " site facility start_date end_date\n", - "0 sgp N1 2011-10-21 2020-06-01" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "print(\"The following locations and date ranges are available for this VAP:\")\n", - "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" - ] - }, - { - "cell_type": "markdown", - "id": "8d132223", - "metadata": {}, - "source": [ - "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "e563983a", - "metadata": {}, - "outputs": [], - "source": [ - "site_facility = ( 'sgp', 'N1' )\n", - "\n", - "date_start = '2020-05-29'\n", - "date_end = '2020-05-31'" - ] - }, - { - "cell_type": "markdown", - "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", - "metadata": {}, - "source": [ - "## Load data files\n", - "Load data files from /data/archive/" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'/data/archive/sgp/sgpsfccldgrid2longcaracenaN1.c1'" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Compile list of files\n", - "site, facility = site_facility\n", - "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", - "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", - "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", - "dir_path\n" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "6be8f3dc", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "['20200529', '20200530', '20200531']" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "from datetime import date, timedelta\n", - "import pandas as pd\n", - "\n", - "def get_ARM_formated_dates(start_date, end_date):\n", - " \"\"\"\n", - " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", - " EXAMPLE:\n", - " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", - " >> [\"20180219\", \"20180220\", \"20180221\"] \n", - " \"\"\"\n", - " \n", - " _start_date = pd.to_datetime(start_date)\n", - " _end_date = pd.to_datetime(end_date)\n", - " \n", - " delta = _end_date - _start_date # returns timedelta \n", - " dates = []\n", - "\n", - " for i in range(delta.days + 1):\n", - " day = _start_date + timedelta(days=i)\n", - " day_formated = day.strftime(format=\"%Y%m%d\")\n", - " dates.append(day_formated)\n", - " return dates\n", - "\n", - "\n", - "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "51feea2e", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "['/data/archive/sgp/sgpsfccldgrid2longcaracenaN1.c1/sgpsfccldgrid2longcaracenaN1.c1.20200529.060000.nc',\n", - " '/data/archive/sgp/sgpsfccldgrid2longcaracenaN1.c1/sgpsfccldgrid2longcaracenaN1.c1.20200530.060000.nc',\n", - " '/data/archive/sgp/sgpsfccldgrid2longcaracenaN1.c1/sgpsfccldgrid2longcaracenaN1.c1.20200531.060000.nc']" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Filter a list of files based on date pattern\n", - "import glob\n", - "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", - "files_filter = []\n", - "for date in dates:\n", - " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", - " files_filter\n", - "files_filter" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "id": "b0e5d0d7", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "3 files loaded\n" - ] - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "
<xarray.Dataset>\n",
-       "Dimensions:                                                         (time: 288,\n",
-       "                                                                     bound: 2,\n",
-       "                                                                     lat: 8,\n",
-       "                                                                     lon: 11)\n",
-       "Coordinates:\n",
-       "  * time                                                            (time) datetime64[ns] ...\n",
-       "  * lat                                                             (lat) float32 ...\n",
-       "  * lon                                                             (lon) float32 ...\n",
-       "Dimensions without coordinates: bound\n",
-       "Data variables: (12/59)\n",
-       "    base_time                                                       (time) datetime64[ns] ...\n",
-       "    time_offset                                                     (time) datetime64[ns] ...\n",
-       "    time_bounds                                                     (time, bound) object dask.array<chunksize=(96, 2), meta=np.ndarray>\n",
-       "    downwelling_shortwave                                           (time, lat, lon) float32 dask.array<chunksize=(96, 8, 11), meta=np.ndarray>\n",
-       "    source_central_facility_downwelling_shortwave                   (time) int32 dask.array<chunksize=(96,), meta=np.ndarray>\n",
-       "    qc_downwelling_shortwave                                        (time, lat, lon) int32 dask.array<chunksize=(96, 8, 11), meta=np.ndarray>\n",
-       "    ...                                                              ...\n",
-       "    qc_visible_cloud_optical_depth                                  (time, lat, lon) int32 dask.array<chunksize=(96, 8, 11), meta=np.ndarray>\n",
-       "    cloud_radiating_temperature                                     (time, lat, lon) float32 dask.array<chunksize=(96, 8, 11), meta=np.ndarray>\n",
-       "    source_central_facility_cloud_radiating_temperature             (time) int32 dask.array<chunksize=(96,), meta=np.ndarray>\n",
-       "    qc_cloud_radiating_temperature                                  (time, lat, lon) int32 dask.array<chunksize=(96, 8, 11), meta=np.ndarray>\n",
-       "    azimuth                                                         (time) float32 dask.array<chunksize=(96,), meta=np.ndarray>\n",
-       "    alt                                                             (time, lat, lon) float32 dask.array<chunksize=(96, 8, 11), meta=np.ndarray>\n",
-       "Attributes: (12/17)\n",
-       "    command_line:          sfccldgrid2long_caracena -s sgp -f N1 -b 20171001 ...\n",
-       "    Conventions:           ARM-1.3\n",
-       "    process_version:       vap-sfccldgrid2long_caracena-1.4-0.el7\n",
-       "    dod_version:           sfccldgrid2longcaracena-c1-1.2\n",
-       "    input_datastreams:     sgpsfccldgrid2longstationN1.c1 : 1.4 : 20200529.06...\n",
-       "    site_id:               sgp\n",
-       "    ...                    ...\n",
-       "    doi:                   10.5439/1393588\n",
-       "    history:               created by user gaustad on machine agate at 2022-0...\n",
-       "    _file_dates:           ['20200529', '20200530', '20200531']\n",
-       "    _file_times:           ['060000', '060000', '060000']\n",
-       "    _datastream:           sgpsfccldgrid2longcaracenaN1.c1\n",
-       "    _arm_standards_flag:   1
" - ], - "text/plain": [ - "\n", - "Dimensions: (time: 288,\n", - " bound: 2,\n", - " lat: 8,\n", - " lon: 11)\n", - "Coordinates:\n", - " * time (time) datetime64[ns] ...\n", - " * lat (lat) float32 ...\n", - " * lon (lon) float32 ...\n", - "Dimensions without coordinates: bound\n", - "Data variables: (12/59)\n", - " base_time (time) datetime64[ns] ...\n", - " time_offset (time) datetime64[ns] ...\n", - " time_bounds (time, bound) object dask.array\n", - " downwelling_shortwave (time, lat, lon) float32 dask.array\n", - " source_central_facility_downwelling_shortwave (time) int32 dask.array\n", - " qc_downwelling_shortwave (time, lat, lon) int32 dask.array\n", - " ... ...\n", - " qc_visible_cloud_optical_depth (time, lat, lon) int32 dask.array\n", - " cloud_radiating_temperature (time, lat, lon) float32 dask.array\n", - " source_central_facility_cloud_radiating_temperature (time) int32 dask.array\n", - " qc_cloud_radiating_temperature (time, lat, lon) int32 dask.array\n", - " azimuth (time) float32 dask.array\n", - " alt (time, lat, lon) float32 dask.array\n", - "Attributes: (12/17)\n", - " command_line: sfccldgrid2long_caracena -s sgp -f N1 -b 20171001 ...\n", - " Conventions: ARM-1.3\n", - " process_version: vap-sfccldgrid2long_caracena-1.4-0.el7\n", - " dod_version: sfccldgrid2longcaracena-c1-1.2\n", - " input_datastreams: sgpsfccldgrid2longstationN1.c1 : 1.4 : 20200529.06...\n", - " site_id: sgp\n", - " ... ...\n", - " doi: 10.5439/1393588\n", - " history: created by user gaustad on machine agate at 2022-0...\n", - " _file_dates: ['20200529', '20200530', '20200531']\n", - " _file_times: ['060000', '060000', '060000']\n", - " _datastream: sgpsfccldgrid2longcaracenaN1.c1\n", - " _arm_standards_flag: 1" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Load files as a single dataset\n", - "files_list = files_filter \n", - "ds = act.io.armfiles.read_netcdf(files_list)\n", - "ds.clean.cleanup()\n", - "print(f'{len(files_list)} files loaded')\n", - "ds\n" - ] - }, - { - "cell_type": "markdown", - "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", - "metadata": {}, - "source": [ - "## Plot time series data\n", - "#### Define the list of variables to be plotted:" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "id": "d09b789e-84f1-4605-846b-a72c110c8048", - "metadata": {}, - "outputs": [], - "source": [ - "variables_to_plot = ['downwelling_shortwave', 'source_central_facility_downwelling_shortwave', 'clearsky_downwelling_shortwave']" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", - "metadata": {}, - "outputs": [ - { - "ename": "TypeError", - "evalue": "Dimensions of C (11, 8, 288) should be one smaller than X(288) and Y(8) while using shading='flat' see help(pcolormesh)", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", - "Cell \u001b[0;32mIn[9], line 5\u001b[0m\n\u001b[1;32m 2\u001b[0m ts_display\u001b[38;5;241m.\u001b[39madd_subplots((\u001b[38;5;28mlen\u001b[39m(variables_to_plot),), figsize \u001b[38;5;241m=\u001b[39m (\u001b[38;5;241m9.5\u001b[39m,\u001b[38;5;241m4\u001b[39m\u001b[38;5;241m*\u001b[39m\u001b[38;5;28mlen\u001b[39m(variables_to_plot)))\n\u001b[1;32m 4\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m i,v \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28menumerate\u001b[39m(variables_to_plot):\n\u001b[0;32m----> 5\u001b[0m ts_ax \u001b[38;5;241m=\u001b[39m \u001b[43mts_display\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mplot\u001b[49m\u001b[43m(\u001b[49m\u001b[43mv\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43msubplot_index\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mi\u001b[49m\u001b[43m,\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mset_title\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mds\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mvariables\u001b[49m\u001b[43m[\u001b[49m\u001b[43mv\u001b[49m\u001b[43m]\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mattrs\u001b[49m\u001b[43m[\u001b[49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43mlong_name\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m,\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 6\u001b[0m ts_ax\u001b[38;5;241m.\u001b[39mgrid()\n\u001b[1;32m 8\u001b[0m plt\u001b[38;5;241m.\u001b[39mshow()\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/act/plotting/timeseriesdisplay.py:588\u001b[0m, in \u001b[0;36mTimeSeriesDisplay.plot\u001b[0;34m(self, field, dsname, subplot_index, cmap, set_title, add_nan, day_night_background, invert_y_axis, abs_limits, time_rng, y_rng, use_var_for_y, set_shading, assessment_overplot, overplot_marker, overplot_behind, overplot_markersize, assessment_overplot_category, assessment_overplot_category_color, force_line_plot, labels, cbar_label, cbar_h_adjust, secondary_y, y_axis_flag_meanings, colorbar_labels, cb_friendly, **kwargs)\u001b[0m\n\u001b[1;32m 586\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124medgecolors\u001b[39m\u001b[38;5;124m'\u001b[39m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;129;01min\u001b[39;00m kwargs\u001b[38;5;241m.\u001b[39mkeys():\n\u001b[1;32m 587\u001b[0m kwargs[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124medgecolors\u001b[39m\u001b[38;5;124m'\u001b[39m] \u001b[38;5;241m=\u001b[39m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mface\u001b[39m\u001b[38;5;124m'\u001b[39m\n\u001b[0;32m--> 588\u001b[0m mesh \u001b[38;5;241m=\u001b[39m \u001b[43max\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mpcolormesh\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 589\u001b[0m \u001b[43m \u001b[49m\u001b[43mnp\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43masarray\u001b[49m\u001b[43m(\u001b[49m\u001b[43mxdata\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 590\u001b[0m \u001b[43m \u001b[49m\u001b[43mydata\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 591\u001b[0m \u001b[43m \u001b[49m\u001b[43mdata\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mtranspose\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 592\u001b[0m \u001b[43m \u001b[49m\u001b[43mshading\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mset_shading\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 593\u001b[0m \u001b[43m \u001b[49m\u001b[43mcmap\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcmap\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 594\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 595\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 597\u001b[0m \u001b[38;5;66;03m# Set Title\u001b[39;00m\n\u001b[1;32m 598\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m set_title \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/matplotlib/__init__.py:1442\u001b[0m, in \u001b[0;36m_preprocess_data..inner\u001b[0;34m(ax, data, *args, **kwargs)\u001b[0m\n\u001b[1;32m 1439\u001b[0m \u001b[38;5;129m@functools\u001b[39m\u001b[38;5;241m.\u001b[39mwraps(func)\n\u001b[1;32m 1440\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21minner\u001b[39m(ax, \u001b[38;5;241m*\u001b[39margs, data\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mNone\u001b[39;00m, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs):\n\u001b[1;32m 1441\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m data \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[0;32m-> 1442\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mfunc\u001b[49m\u001b[43m(\u001b[49m\u001b[43max\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;28;43mmap\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43msanitize_sequence\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43margs\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 1444\u001b[0m bound \u001b[38;5;241m=\u001b[39m new_sig\u001b[38;5;241m.\u001b[39mbind(ax, \u001b[38;5;241m*\u001b[39margs, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs)\n\u001b[1;32m 1445\u001b[0m auto_label \u001b[38;5;241m=\u001b[39m (bound\u001b[38;5;241m.\u001b[39marguments\u001b[38;5;241m.\u001b[39mget(label_namer)\n\u001b[1;32m 1446\u001b[0m \u001b[38;5;129;01mor\u001b[39;00m bound\u001b[38;5;241m.\u001b[39mkwargs\u001b[38;5;241m.\u001b[39mget(label_namer))\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/matplotlib/axes/_axes.py:6220\u001b[0m, in \u001b[0;36mAxes.pcolormesh\u001b[0;34m(self, alpha, norm, cmap, vmin, vmax, shading, antialiased, *args, **kwargs)\u001b[0m\n\u001b[1;32m 6217\u001b[0m shading \u001b[38;5;241m=\u001b[39m shading\u001b[38;5;241m.\u001b[39mlower()\n\u001b[1;32m 6218\u001b[0m kwargs\u001b[38;5;241m.\u001b[39msetdefault(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124medgecolors\u001b[39m\u001b[38;5;124m'\u001b[39m, \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mnone\u001b[39m\u001b[38;5;124m'\u001b[39m)\n\u001b[0;32m-> 6220\u001b[0m X, Y, C, shading \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_pcolorargs\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43mpcolormesh\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 6221\u001b[0m \u001b[43m \u001b[49m\u001b[43mshading\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mshading\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mkwargs\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 6222\u001b[0m coords \u001b[38;5;241m=\u001b[39m np\u001b[38;5;241m.\u001b[39mstack([X, Y], axis\u001b[38;5;241m=\u001b[39m\u001b[38;5;241m-\u001b[39m\u001b[38;5;241m1\u001b[39m)\n\u001b[1;32m 6223\u001b[0m \u001b[38;5;66;03m# convert to one dimensional array, except for 3D RGB(A) arrays\u001b[39;00m\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/matplotlib/axes/_axes.py:5751\u001b[0m, in \u001b[0;36mAxes._pcolorargs\u001b[0;34m(self, funcname, shading, *args, **kwargs)\u001b[0m\n\u001b[1;32m 5749\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m shading \u001b[38;5;241m==\u001b[39m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mflat\u001b[39m\u001b[38;5;124m'\u001b[39m:\n\u001b[1;32m 5750\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m (Nx, Ny) \u001b[38;5;241m!=\u001b[39m (ncols \u001b[38;5;241m+\u001b[39m \u001b[38;5;241m1\u001b[39m, nrows \u001b[38;5;241m+\u001b[39m \u001b[38;5;241m1\u001b[39m):\n\u001b[0;32m-> 5751\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mTypeError\u001b[39;00m(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mDimensions of C \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mC\u001b[38;5;241m.\u001b[39mshape\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m should\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 5752\u001b[0m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m be one smaller than X(\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mNx\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m) and Y(\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mNy\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m)\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 5753\u001b[0m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m while using shading=\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mflat\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 5754\u001b[0m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m see help(\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mfuncname\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m)\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[1;32m 5755\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m: \u001b[38;5;66;03m# ['nearest', 'gouraud']:\u001b[39;00m\n\u001b[1;32m 5756\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m (Nx, Ny) \u001b[38;5;241m!=\u001b[39m (ncols, nrows):\n", - "\u001b[0;31mTypeError\u001b[0m: Dimensions of C (11, 8, 288) should be one smaller than X(288) and Y(8) while using shading='flat' see help(pcolormesh)" - ] - }, - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "dd49c62706534d8988ce7d6a5c25f646", - "version_major": 2, - "version_minor": 0 - }, - "image/png": "iVBORw0KGgoAAAANSUhEUgAAA7YAAASwCAYAAADPBNYLAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAA9hAAAPYQGoP6dpAABi0UlEQVR4nO3df3RV5Z3o/08gkKA1sUAJIIjRaqXlqkO4IrF8Ha3GQceWGeeKY5eog/c2Vy0DqV5FZvmD5axMO6tO6w9Qr6DjXWgz/hxmbkbNzFhFwRlJg+MIrb1CDWgiTRwT1DYI7O8fLjJNE5Rf5yQPvF5rnT/Ow7OTZ283cb/Z5+QUZFmWBQAAACRqUH8vAAAAAPaHsAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAnbHHvhhRfiggsuiLFjx0ZBQUE89dRTn7nN888/HxUVFVFcXBzHHnts3HPPPblfKAAAQKKEbY59+OGHcfLJJ8ddd921R/M3btwY5513XkyfPj2amprixhtvjLlz58bjjz+e45UCAACkqSDLsqy/F3GoKCgoiCeffDJmzpy52znXX399rFixItavX989Vl1dHa+++mqsXr06D6sEAABIS2F/L4CeVq9eHVVVVT3Gzj333Fi6dGl8/PHHMWTIkD636+rqiq6uru7nO3fujPfeey9GjBgRBQUFOV0zAAAc6rIsi61bt8bYsWNj0CAvjM03YTvAtLa2RllZWY+xsrKy2L59e7S1tcWYMWP63K62tjZuvfXWfCwRAADYjU2bNsW4ceP6exmHHGE7AP32HdZdrxb/tDuvCxYsiJqamu7nHR0dcfTRR8emTZuipKQkNwsFAAAiIqKzszPGjx8fRxxxRH8v5ZAkbAeY0aNHR2tra4+xLVu2RGFhYYwYMWK32xUVFUVRUVGv8ZKSEmELAAB54m2A/cOLvweYadOmRUNDQ4+xZ599NqZMmbLb99cCAAAcyoRtjn3wwQexdu3aWLt2bUR88nE+a9eujebm5oj45CXEs2fP7p5fXV0db731VtTU1MT69etj2bJlsXTp0rj22mv7Y/kAAAADnpci59iaNWvizDPP7H6+632wl112WTz44IPR0tLSHbkREeXl5VFfXx/z58+Pu+++O8aOHRt33HFHXHjhhXlfOwAAQAp8ju1BqrOzM0pLS6Ojo8N7bAEAIMdcf/cvL0UGAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbPNk8eLFUV5eHsXFxVFRURErV6781PnLly+Pk08+OQ477LAYM2ZMXHHFFdHe3p6n1QIAAKRD2OZBXV1dzJs3LxYuXBhNTU0xffr0mDFjRjQ3N/c5/8UXX4zZs2fHnDlz4vXXX49HH300XnnllbjyyivzvHIAAICBT9jmwe233x5z5syJK6+8MiZOnBg/+MEPYvz48bFkyZI+57/88stxzDHHxNy5c6O8vDy++tWvxre+9a1Ys2ZNnlcOAAAw8AnbHNu2bVs0NjZGVVVVj/GqqqpYtWpVn9tUVlbG5s2bo76+PrIsi3fffTcee+yxOP/883f7fbq6uqKzs7PHAwAA4FAgbHOsra0tduzYEWVlZT3Gy8rKorW1tc9tKisrY/ny5TFr1qwYOnRojB49Oo488si48847d/t9amtro7S0tPsxfvz4A7ofAAAAA5WwzZOCgoIez7Ms6zW2y7p162Lu3Llx0003RWNjYzz99NOxcePGqK6u3u3XX7BgQXR0dHQ/Nm3adEDXDwAAMFAV9vcCDnYjR46MwYMH97o7u2XLll53cXepra2N008/Pa677rqIiDjppJPi8MMPj+nTp8dtt90WY8aM6bVNUVFRFBUVHfgdAAAAGODcsc2xoUOHRkVFRTQ0NPQYb2hoiMrKyj63+eijj2LQoJ7/aQYPHhwRn9zpBQAA4D8J2zyoqamJ+++/P5YtWxbr16+P+fPnR3Nzc/dLixcsWBCzZ8/unn/BBRfEE088EUuWLIkNGzbESy+9FHPnzo1TTz01xo4d21+7AQAAMCB5KXIezJo1K9rb22PRokXR0tISkyZNivr6+pgwYUJERLS0tPT4TNvLL788tm7dGnfddVd85zvfiSOPPDLOOuus+O53v9tfuwAAADBgFWRe23pQ6uzsjNLS0ujo6IiSkpL+Xg4AABzUXH/3Ly9FBgAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmzzZPHixVFeXh7FxcVRUVERK1eu/NT5XV1dsXDhwpgwYUIUFRXFcccdF8uWLcvTagEAANJR2N8LOBTU1dXFvHnzYvHixXH66afHvffeGzNmzIh169bF0Ucf3ec2F110Ubz77ruxdOnS+OIXvxhbtmyJ7du353nlAAAAA19BlmVZfy/iYDd16tSYPHlyLFmypHts4sSJMXPmzKitre01/+mnn46LL744NmzYEMOHD9+n79nZ2RmlpaXR0dERJSUl+7x2AADgs7n+7l9eipxj27Zti8bGxqiqquoxXlVVFatWrepzmxUrVsSUKVPie9/7Xhx11FFxwgknxLXXXhu/+tWv8rFkAACApHgpco61tbXFjh07oqysrMd4WVlZtLa29rnNhg0b4sUXX4zi4uJ48skno62tLa666qp47733dvs+266urujq6up+3tnZeeB2AgAAYABzxzZPCgoKejzPsqzX2C47d+6MgoKCWL58eZx66qlx3nnnxe233x4PPvjgbu/a1tbWRmlpafdj/PjxB3wfAAAABiJhm2MjR46MwYMH97o7u2XLll53cXcZM2ZMHHXUUVFaWto9NnHixMiyLDZv3tznNgsWLIiOjo7ux6ZNmw7cTgAAAAxgwjbHhg4dGhUVFdHQ0NBjvKGhISorK/vc5vTTT4933nknPvjgg+6xN954IwYNGhTjxo3rc5uioqIoKSnp8QAAADgUCNs8qKmpifvvvz+WLVsW69evj/nz50dzc3NUV1dHxCd3W2fPnt09/5JLLokRI0bEFVdcEevWrYsXXnghrrvuuviTP/mTGDZsWH/tBgAAwIDkl0fl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pEmTor6+PiZMmBARES0tLT0+0/bee++N7du3x9VXXx1XX3119/hll10WDz74YL6XDwAAMKD5HNuDlM/RAgCA/HH93b+8xxYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwBQAAIGnCFgAAgKQJWwAAAJImbAEAAEiasAUAACBpwhYAAICkCVsAAACSJmwBAABImrAFAAAgacIWAACApAlbAAAAkiZsAQAASJqwzZPFixdHeXl5FBcXR0VFRaxcufJT5z///PNRUVERxcXFceyxx8Y999yTp5UCAACkRdjmQV1dXcybNy8WLlwYTU1NMX369JgxY0Y0Nzf3OX/jxo1x3nnnxfTp06OpqSluvPHGmDt3bjz++ON5XjkAAMDAV5BlWdbfizjYTZ06NSZPnhxLlizpHps4cWLMnDkzamtre82//vrrY8WKFbF+/fruserq6nj11Vdj9erVe/Q9Ozs7o7S0NDo6OqKkpGT/dwIAANgt19/9yx3bHNu2bVs0NjZGVVVVj/GqqqpYtWpVn9usXr261/xzzz031qxZEx9//HHO1goAAJCiwv5ewMGura0tduzYEWVlZT3Gy8rKorW1tc9tWltb+5y/ffv2aGtrizFjxvTapqurK7q6urqfd3R0RMQn/3IEAADk1q7rbi+I7R/CNk8KCgp6PM+yrNfYZ83va3yX2trauPXWW3uNjx8/fm+XCgAA7KP29vYoLS3t72UccoRtjo0cOTIGDx7c6+7sli1bet2V3WX06NF9zi8sLIwRI0b0uc2CBQuipqam+/n7778fEyZMiObmZn+xPkVnZ2eMHz8+Nm3a5L0Qu+EY7RnH6bM5RnvGcdozjtNnc4z2jOP02RyjPdPR0RFHH310DB8+vL+XckgStjk2dOjQqKioiIaGhviDP/iD7vGGhob4xje+0ec206ZNi7/7u7/rMfbss8/GlClTYsiQIX1uU1RUFEVFRb3GS0tL/QDaAyUlJY7TZ3CM9ozj9Nkcoz3jOO0Zx+mzOUZ7xnH6bI7Rnhk0yK8x6g+Oeh7U1NTE/fffH8uWLYv169fH/Pnzo7m5OaqrqyPik7uts2fP7p5fXV0db731VtTU1MT69etj2bJlsXTp0rj22mv7axcAAAAGLHds82DWrFnR3t4eixYtipaWlpg0aVLU19fHhAkTIiKipaWlx2falpeXR319fcyfPz/uvvvuGDt2bNxxxx1x4YUX9tcuAAAADFjCNk+uuuqquOqqq/r8swcffLDX2BlnnBE/+clP9vn7FRUVxc0339zny5P5T47TZ3OM9ozj9Nkcoz3jOO0Zx+mzOUZ7xnH6bI7RnnGc+ldB5vdRAwAAkDDvsQUAACBpwhYAAICkCVsAAACSJmwHqNra2viv//W/xhFHHBGjRo2KmTNnxs9+9rMec7Isi1tuuSXGjh0bw4YNi9/93d+N119/vcecrq6u+Pa3vx0jR46Mww8/PL7+9a/H5s2be8z5j//4j7j00kujtLQ0SktL49JLL433338/17t4QOTzOP35n/95VFZWxmGHHRZHHnlkrnftgMnXMfrFL34Rc+bMifLy8hg2bFgcd9xxcfPNN8e2bdvysp/7K5/n0te//vU4+uijo7i4OMaMGROXXnppvPPOOznfx/2Vz2P0m3NPOeWUKCgoiLVr1+Zq1w6ofB6nY445JgoKCno8brjhhpzv44GQ7/Pp//7f/xtTp06NYcOGxciRI+MP//APc7p/B0K+jtGPf/zjXufRrscrr7ySl33dH/k8l9544434xje+ESNHjoySkpI4/fTT47nnnsv5Ph4I+TxOP/nJT+Kcc86JI488MkaMGBH/43/8j/jggw9yvo/760Ado/vuuy9+93d/N0pKSqKgoKDP6+qUr78HrIwB6dxzz80eeOCB7N///d+ztWvXZueff3529NFHZx988EH3nL/4i7/IjjjiiOzxxx/PXnvttWzWrFnZmDFjss7Ozu451dXV2VFHHZU1NDRkP/nJT7IzzzwzO/nkk7Pt27d3z/m93/u9bNKkSdmqVauyVatWZZMmTcp+//d/P6/7u6/yeZxuuumm7Pbbb89qamqy0tLSfO7mfsnXMfqHf/iH7PLLL8+eeeaZ7M0338z+9m//Nhs1alT2ne98J+/7vC/yeS7dfvvt2erVq7Nf/OIX2UsvvZRNmzYtmzZtWl73d1/k8xjtMnfu3GzGjBlZRGRNTU352M39ls/jNGHChGzRokVZS0tL92Pr1q153d99lc/j9Nhjj2Wf//znsyVLlmQ/+9nPsp/+9KfZo48+mtf93Rf5OkZdXV09zqGWlpbsyiuvzI455phs586ded/vvZXPc+mLX/xidt5552Wvvvpq9sYbb2RXXXVVdthhh2UtLS153ed9ka/j9Pbbb2ef//zns+rq6uynP/1p9q//+q9ZZWVlduGFF+Z9n/fWgTpGf/VXf5XV1tZmtbW1WURk//Ef/9Hre6V8/T1QCdtEbNmyJYuI7Pnnn8+yLMt27tyZjR49OvuLv/iL7jm//vWvs9LS0uyee+7JsizL3n///WzIkCHZj370o+45b7/9djZo0KDs6aefzrIsy9atW5dFRPbyyy93z1m9enUWEdlPf/rTfOzaAZWr4/SbHnjggaTC9rfl4xjt8r3vfS8rLy/P0Z7kVj6P09/+7d9mBQUF2bZt23K0N7mR62NUX1+fnXjiidnrr7+eVNj+tlwepwkTJmR/9Vd/lZ8dybFcHaePP/44O+qoo7L7778/j3uTG/n6ubRt27Zs1KhR2aJFi3K4N7mTq+P0y1/+MouI7IUXXuie09nZmUVE9o//+I/52LUDKlfH6d57781GjRqV7dixo3tOU1NTFhHZz3/+83zs2gGzL8foNz333HN9hu3Bdv09UHgpciI6OjoiImL48OEREbFx48ZobW2Nqqqq7jlFRUVxxhlnxKpVqyIiorGxMT7++OMec8aOHRuTJk3qnrN69eooLS2NqVOnds857bTTorS0tHtOSnJ1nA4m+TxGHR0d3d8nNfk6Tu+9914sX748KisrY8iQIbnanZzI5TF6991347//9/8e/+f//J847LDD8rE7OZPrc+m73/1ujBgxIk455ZT48z//82Re/v/bcnWcfvKTn8Tbb78dgwYNit/5nd+JMWPGxIwZM3q9dDAF+fq5tGLFimhra4vLL788R3uSW7k6TiNGjIiJEyfGQw89FB9++GFs37497r333igrK4uKiop87d4Bk6vj1NXVFUOHDo1Bg/4zM4YNGxYRES+++GJud+oA25djtCcOtuvvgULYJiDLsqipqYmvfvWrMWnSpIiIaG1tjYiIsrKyHnPLysq6/6y1tTWGDh0an//85z91zqhRo3p9z1GjRnXPSUUuj9PBIp/H6M0334w777wzqqurD/Ru5Fw+jtP1118fhx9+eIwYMSKam5vjb//2b3O1OzmRy2OUZVlcfvnlUV1dHVOmTMn1ruRUrs+lP/3TP40f/ehH8dxzz8U111wTP/jBD+Kqq67K5S7lRC6P04YNGyIi4pZbbok/+7M/i7//+7+Pz3/+83HGGWfEe++9l9P9OpDy+fN76dKlce6558b48eMP9G7kXC6PU0FBQTQ0NERTU1McccQRUVxcHH/1V38VTz/9dFK/eyMit8fprLPOitbW1vjLv/zL2LZtW/zHf/xH3HjjjRER0dLSktP9OpD29RjtiYPp+nsgEbYJuOaaa+Lf/u3f4pFHHun1ZwUFBT2eZ1nWa+y3/facvubvydcZaHJ9nA4G+TpG77zzTvze7/1e/Lf/9t/iyiuv3L9F94N8HKfrrrsumpqa4tlnn43BgwfH7NmzI8uy/V98nuTyGN15553R2dkZCxYsOHAL7ie5Ppfmz58fZ5xxRpx00klx5ZVXxj333BNLly6N9vb2A7MDeZLL47Rz586IiFi4cGFceOGFUVFREQ888EAUFBTEo48+eoD2IPfy9fN78+bN8cwzz8ScOXP2b8H9JJfHKcuyuOqqq2LUqFGxcuXK+Nd//df4xje+Eb//+7+fVLBF5PY4feUrX4m//uu/ju9///tx2GGHxejRo+PYY4+NsrKyGDx48IHbiRw70Mfos77Gvn4d/pOwHeC+/e1vx4oVK+K5556LcePGdY+PHj06IqLXv+ps2bKl+1+RRo8e3f0vZZ8259133+31fX/5y1/2+teogSzXx+lgkK9j9M4778SZZ54Z06ZNi/vuuy8Xu5JT+TpOI0eOjBNOOCHOOeec+NGPfhT19fXx8ssv52KXDrhcH6N//ud/jpdffjmKioqisLAwvvjFL0ZExJQpU+Kyyy7L2X4daP3xc+m0006LiIj/9//+3wHZh3zI9XEaM2ZMRER8+ctf7v7zoqKiOPbYY6O5ufnA71AO5PNceuCBB2LEiBHx9a9//UDvRs7l42fT3//938ePfvSjOP3002Py5MmxePHiGDZsWPz1X/91LnftgMrH+XTJJZdEa2trvP3229He3h633HJL/PKXv4zy8vJc7dYBtT/HaE8cLNffA05O38HLPtu5c2d29dVXZ2PHjs3eeOONPv989OjR2Xe/+93usa6urj7f4F9XV9c955133unzl0f9y7/8S/ecl19+OZk3r+frOP2m1H55VD6P0ebNm7Pjjz8+u/jii/v8DbcDWX+cS7s0NzdnEZE999xzB26HciBfx+itt97KXnvtte7HM888k0VE9thjj2WbNm3K8V7uv/48l/7u7/4ui4jsrbfeOoB7lBv5Ok4dHR1ZUVFRj18eteuXI91777252r0DIt/n0s6dO7Py8vJkfpv9Lvk6TitWrMgGDRrU6zePn3DCCdmf//mf52LXDqj+/Nm0dOnS7LDDDuvztwMPJAfiGP2mz/rlUalefw9UwnaA+p//839mpaWl2Y9//OMev37/o48+6p7zF3/xF1lpaWn2xBNPZK+99lr2x3/8x33+SvZx48Zl//iP/5j95Cc/yc4666w+P+7npJNOylavXp2tXr06+y//5b8k8+vG83mc3nrrraypqSm79dZbs8997nNZU1NT1tTUNOA/WiNfx+jtt9/OvvjFL2ZnnXVWtnnz5h7fKwX5Ok7/8i//kt15551ZU1NT9otf/CL753/+5+yrX/1qdtxxx2W//vWv877feyOff99+08aNG5P6rcj5Ok6rVq3Kbr/99qypqSnbsGFDVldXl40dOzb7+te/nvd93hf5PJ/+9E//NDvqqKOyZ555JvvpT3+azZkzJxs1alT23nvv5XWf91a+/8794z/+YxYR2bp16/K2jwdCvo7TL3/5y2zEiBHZH/7hH2Zr167Nfvazn2XXXnttNmTIkGzt2rV53++9lc/z6c4778waGxuzn/3sZ9ldd92VDRs2LPvhD3+Y1/3dFwfqGLW0tGRNTU3Z//7f/7v7N2k3NTVl7e3t3XNSvv4eqITtABURfT4eeOCB7jk7d+7Mbr755mz06NFZUVFR9v/9f/9f9tprr/X4Or/61a+ya665Jhs+fHg2bNiw7Pd///ez5ubmHnPa29uzb37zm9kRRxyRHXHEEdk3v/nNAf8varvk8zhddtllfX6vgX6XLV/H6IEHHtjt90pBvo7Tv/3bv2VnnnlmNnz48KyoqCg75phjsurq6mzz5s352tV9ls+/b78ptbDN13FqbGzMpk6dmpWWlmbFxcXZl770pezmm2/OPvzww3zt6n7J5/m0bdu27Dvf+U42atSo7IgjjsjOPvvs7N///d/zsZv7Jd9/5/74j/84q6yszPVu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9tlnY8qUKTFkyJA+tykqKoqioqJe46WlpX5Ic0CUlJQ4lzggnEscSM4nDhTnEgfKoEF+jVF/cNTzoKamJu6///5YtmxZrF+/PubPnx/Nzc1RXV0dEZ/cbZ09e3b3/Orq6njrrbeipqYm1q9fH8uWLYulS5fGtdde21+7AAAAMGC5Y5sHs2bNivb29li0aFG0tLTEpEmTor6+PiZMmBARES0tLT0+07a8vDzq6+tj/vz5cffdd8fYsWPjjjvuiAsvvLC/dgEAAGDAErZ5ctVVV8VVV13V5589+OCDvcbOOOOM+MlPfrLP36+oqChuvvnmPl+eDHvDucSB4lziQHI+caA4lzhQnEv9qyDz+6gBAABImPfYAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2CZs8eLFUV5eHsXFxVFRURErV6781PnPP/98VFRURHFxcRx77LFxzz335GmlDHR7cy498cQTcc4558QXvvCFKCkpiWnTpsUzzzyTx9UykO3tz6VdXnrppSgsLIxTTjkltwskGXt7LnV1dcXChQtjwoQJUVRUFMcdd1wsW7YsT6tloNvb82n58uVx8sknx2GHHRZjxoyJK664Itrb2/O0WgaqF154IS644IIYO3ZsFBQUxFNPPfWZ27j+zh9hm6i6urqYN29eLFy4MJqammL69OkxY8aMHp+H+5s2btwY5513XkyfPj2amprixhtvjLlz58bjjz+e55Uz0OztufTCCy/EOeecE/X19dHY2BhnnnlmXHDBBdHU1JTnlTPQ7O25tEtHR0fMnj07vva1r+VppQx0+3IuXXTRRfFP//RPsXTp0vjZz34WjzzySJx44ol5XDUD1d6eTy+++GLMnj075syZE6+//no8+uij8corr8SVV16Z55Uz0Hz44Ydx8sknx1133bVH811/51lGkk499dSsurq6x9iJJ56Y3XDDDX3O/1//639lJ554Yo+xb33rW9lpp52WszWShr09l/ry5S9/Obv11lsP9NJIzL6eS7Nmzcr+7M/+LLv55puzk08+OYcrJBV7ey79wz/8Q1ZaWpq1t7fnY3kkZm/Pp7/8y7/Mjj322B5jd9xxRzZu3LicrZH0RET25JNPfuoc19/55Y5tgrZt2xaNjY1RVVXVY7yqqipWrVrV5zarV6/uNf/cc8+NNWvWxMcff5yztTKw7cu59Nt27twZ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atSo3c6rra2N0tLS7sf48eP3e80AAAApELY5NnLkyBg8eHCvu7NbtmzpdRf3t9XV1cWcOXPib/7mb+Lss8/+1LkLFiyIjo6O7semTZv2e+0AAAApELY5NnTo0KioqIiGhoYe4w0NDVFZWbnb7R555JG4/PLL4+GHH47zzz//M79PUVFRlJSU9HgAAAAcCrzHNg9qamri0ksvjSlTpsS0adPivvvui+bm5qiuro6IT+62vv322/HQQw9FxCdRO3v27PjhD38Yp512Wvfd3mHDhkVpaWm/7QcAAMBAJGzzYNasWdHe3h6LFi2KlpaWmDRpUtTX18eECRMiIqKlpaXHZ9ree++9sX379rj66qvj6quv7h6/7LLL4sEHH8z38gEAAAY0n2N7kPI5WgAAkD+uv/uX99gCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2ObJ4sWLo7y8PIqLi6OioiJWrlz5qfOff/75qKioiOLi4jj22GPjnnvuydNKAQAA0iJs86Curi7mzZsXCxcujKamppg+fXrMmDEjmpub+5y/cePGOO+882L69OnR1NQUN954Y8ydOzcef/zxPK8cAABg4CvIsizr70Uc7KZOnRqTJ0+OJUuWdI9NnDgxZs6cGbW1tb3mX3/99bFixYpYv35991h1dXW8+uqrsXr16j36np2dnVFaWhodHR1RUlKy/zsBAADsluvv/lXY3ws42G3bti0aGxvjhhtu6DFeVVUVq1at6nOb1atXR1VVVY+xc889N5YuXRoff/xxDBkypNc2XV1d0dXV1f28o6MjIj75CwYAAOTWrutu9w37h7DNsba2ttixY0eUlZX1GC8rK4vW1tY+t2ltbe1z/vbt26OtrS3GjBnTa5va2tq49dZbe42PHz9+P1YPAADsjfb29igtLe3vZRxyhG2eFBQU9HieZVmvsc+a39f4LgsWLIiampru5++//35MmDAhmpub/cViv3R2dsb48eNj06ZNXlbDfnEucSA5nzhQnEscKB0dHXH00UfH8OHD+3sphyRhm2MjR46MwYMH97o7u2XLll53ZXcZPXp0n/MLCwtjxIgRfW5TVFQURUVFvcZLS0v9kOaAKCkpcS5xQDiXOJCcTxwoziUOlEGD/H7e/uCo59jQoUOjoqIiGhoaeow3NDREZWVln9tMmzat1/xnn302pkyZ0uf7awEAAA5lwjYPampq4v77749ly5bF+vXrY/78+dHc3BzV1dUR8cnLiGfPnt09v7q6Ot56662oqamJ9evXx7Jly2Lp0qVx7bXX9tcuAAAADFheipwHs2bNivb29li0aFG0tLTEpEmTor6+PiZMmBARES0tLT0+07a8vDzq6+tj/vz5cffdd8fYsWPjjjvuiAsvvHCPv2dRUVHcfPPNfb48GfaGc4kDxbnEgeR84kBxLnGgOJf6l8+xBQAAIGleigwAAEDShC0AAABJE7YAAAAkTdgCAACQNGGbsMWLF0d5eXkUFxdHRUVFrFy58lPnP//881FRURHFxcVx7LHHxj333JOnlTLQ7c259MQTT8Q555wTX/jCF6KkpCSmTZsWzzzzTB5Xy0C2tz+XdnnppZeisLAwTjnllNwukGTs7bnU1dUVCxcujAkTJkRRUVEcd9xxsWzZsjytloFub8+n5cuXx8knnxyHHXZYjBkzJq644opob2/P02oZqF544YW44IILYuzYsVFQUBBPPfXUZ27j+jt/hG2i6urqYt68ebFw4cJoamqK6dOnx4wZM3p8bNBv2rhxY5x33nkxffr0aGpqihtvvDHmzp0bjz/+eJ5XzkCzt+fSCy+8EOecc07U19dHY2NjnHnmmXHBBRdEU1NTnlfOQLO359IuHR0dMXv27Pja176Wp5Uy0O3LuXTRRRfFP/3TP8XSpUvjZz/7WTzyyCNx4okn5nHVDFR7ez69+OKLMXv27JgzZ068/vrr8eijj8Yrr7wSV155ZZ5XzkDz4Ycfxsknnxx33XXXHs13/Z1nGUk69dRTs+rq6h5jJ554YnbDDTf0Of9//a//lZ144ok9xr71rW9lp512Ws7WSBr29lzqy5e//OXs1ltvPdBLIzH7ei7NmjUr+7M/+7Ps5ptvzk4++eQcrpBU7O259A//8A9ZaWlp1t7eno/lkZi9PZ/+8i//Mjv22GN7jN1xxx3ZuHHjcrZG0hMR2ZNPPvmpc1x/55c7tgnatm1bNDY2RlVVVY/xqqqqWLVqVZ/brF69utf8c889N9asWRMff/xxztbKwLYv59Jv27lzZ2zdujWGDx+eiyWSiH09lx544IF488034+abb871EknEvpxLK1asiClTpsT3vve9OOqoo+KEE06Ia6+9Nn71q1/lY8kMYPtyPlVWVsbmzZujvr4+siyLd999Nx577LE4//zz87FkDiKuv/OrsL8XwN5ra2uLHTt2RFlZWY/xsrKyaG1t7XOb1tbWPudv37492traYsyYMTlbLwPXvpxLv+373/9+fPjhh3HRRRflYokkYl/OpZ///Odxww03xMqVK6Ow0P+O+MS+nEsbNmyIF198MYqLi+PJJ5+Mtra2uOqqq+K9997zPttD3L6cT5WVlbF8+fKYNWtW/PrXv47t27fH17/+9bjzzjvzsWQOIq6/88sd24QVFBT0eJ5lWa+xz5rf1ziHnr09l3Z55JFH4pZbbom6uroYNWpUrpZHQvb0XNqxY0dccsklceutt8YJJ5yQr+WRkL35ubRz584oKCiI5cuXx6mnnhrnnXde3H777fHggw+6a0tE7N35tG7dupg7d27cdNNN0djYGE8//XRs3Lgxqqur87FUDjKuv/PHP5EnaOTIkTF48OBe/9K4ZcuWXv8qtMvo0aP7nF9YWBgjRozI2VoZ2PblXNqlrq4u5syZE48++micffbZuVwmCdjbc2nr1q2xZs2aaGpqimuuuSYiPomTLMuisLAwnn322TjrrLPysnYGln35uTRmzJg46qijorS0tHts4sSJkWVZbN68OY4//vicrpmBa1/Op9ra2jj99NPjuuuui4iIk046KQ4//PCYPn163Hbbbe6yscdcf+eXO7YJGjp0aFRUVERDQ0OP8YaGhqisrOxzm2nTpvWa/+yzz8aUKVNiyJAhOVsrA9u+nEsRn9ypvfzyy+Phhx/2niMiYu/PpZKSknjttddi7dq13Y/q6ur40pe+FGvXro2pU6fma+kMMPvyc+n000+Pd955Jz744IPusTfeeCMGDRoU48aNy+l6Gdj25Xz66KOPYtCgnpfIgwcPjoj/vNsGe8L1d5710y+tYj/96Ec/yoYMGZItXbo0W7duXTZv3rzs8MMPz37xi19kWZZlN9xwQ3bppZd2z9+wYUN22GGHZfPnz8/WrVuXLV26NBsyZEj22GOP9dcuMEDs7bn08MMPZ4WFhdndd9+dtbS0dD/ef//9/toFBoi9PZd+m9+KzC57ey5t3bo1GzduXPZHf/RH2euvv549//zz2fHHH59deeWV/bULDCB7ez498MADWWFhYbZ48eLszTffzF588cVsypQp2amnntpfu8AAsXXr1qypqSlramrKIiK7/fbbs6ampuytt97Kssz1d38Ttgm7++67swkTJmRDhw7NJk+enD3//PPdf3bZZZdlZ5xxRo/5P/7xj7Pf+Z3fyYYOHZodc8wx2ZIlS/K8YgaqvTmXzjjjjCwiej0uu+yy/C+cAWdvfy79JmHLb9rbc2n9+vXZ2WefnQ0bNiwbN25cVlNTk3300Ud5XjUD1d6eT3fccUf25S9/ORs2bFg2ZsyY7Jvf/Ga2efPmPK+agea555771Gsg19/9qyDLvKYCAACAdHmPLQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACRN2AIAAJA0YQsAAEDShC0AAABJE7YAAAAkTdgCAACQNGELAABA0oQtAAAASRO2AAAAJE3YAgAAkDRhCwAAQNKELQAAAEkTtgAAACTt/wcLAF7/XlacegAAAABJRU5ErkJggg==", - "text/html": [ - "\n", - "
\n", - "
\n", - " Figure\n", - "
\n", - " \n", - "
\n", - " " - ], - "text/plain": [ - "Canvas(toolbar=Toolbar(toolitems=[('Home', 'Reset original view', 'home', 'home'), ('Back', 'Back to previous …" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", - "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", - "\n", - "for i,v in enumerate(variables_to_plot):\n", - " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", - " ts_ax.grid()\n", - "\n", - "plt.show()\n" - ] - }, - { - "cell_type": "markdown", - "id": "194399aa-1907-452b-8ba9-bc31d7f60291", - "metadata": {}, - "source": [ - "## Quality check plots\n", - "#### Define variable for QC plot" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", - "metadata": {}, - "outputs": [], - "source": [ - "qc_variable = 'downwelling_shortwave'" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", - "metadata": {}, - "outputs": [], - "source": [ - "# QC Plot\n", - "if ('qc_'+qc_variable) in ds.variables:\n", - "\n", - " # Plot\n", - " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", - " qc_display.add_subplots((2,), figsize = (9.5,10))\n", - " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", - " qc_ax.grid()\n", - " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", - "\n", - " plt.show()\n", - "else:\n", - " print(f'QC not available for the selected field: {qc_variable}')\n" - ] - }, - { - "cell_type": "markdown", - "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", - "metadata": {}, - "source": [ - "## Field selection dropdown menu\n", - "Select variable to be plotted from a dropdown menu" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", - "metadata": {}, - "outputs": [], - "source": [ - "plt.ioff()\n", - "\n", - "# populate dropdown menu with available variables \n", - "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", - "d_variable = 'downwelling_shortwave'\n", - "dropdown = widgets.Dropdown(\n", - " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", - " value= d_variable,\n", - " description='Field:',\n", - " disabled=False,\n", - ")\n", - "dropdown.layout.margin = '0px 30% 0px 20%'\n", - "dropdown.layout.width = '50%'\n", - "\n", - "# set up display\n", - "i_display = act.plotting.TimeSeriesDisplay(ds)\n", - "i_display.add_subplots((1,), figsize = (9.5,5))\n", - "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", - "i_ax.grid()\n", - "i_fig = i_display.fig\n", - "\n", - "# update plot callback function\n", - "def update_plot(change):\n", - " i_ax.cla()\n", - " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", - " i_ax_new.grid()\n", - " i_fig.canvas.draw()\n", - " i_fig.canvas.flush_events()\n", - "\n", - "dropdown.observe(update_plot, names='value')\n", - "\n", - "widgets.AppLayout(\n", - " header=dropdown,\n", - " center=i_fig.canvas,\n", - " pane_heights=[1, 6,1]\n", - ")\n" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.16" - }, - "widgets": { - "application/vnd.jupyter.widget-state+json": { - "state": {}, - "version_major": 2, - "version_minor": 0 - } - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/VAPs/quicklook/SONDEADJUST/.ipynb_checkpoints/SONDEADJUST_tutorial-checkpoint.ipynb b/VAPs/quicklook/SONDEADJUST/.ipynb_checkpoints/SONDEADJUST_tutorial-checkpoint.ipynb deleted file mode 100644 index 698be192..00000000 --- a/VAPs/quicklook/SONDEADJUST/.ipynb_checkpoints/SONDEADJUST_tutorial-checkpoint.ipynb +++ /dev/null @@ -1,4058 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "70840257-70e4-45e2-b491-14bff5a257a3", - "metadata": {}, - "source": [ - "# SONDEADJUST.C1 Notebook\n", - "\n", - "[Click here](https://www.arm.gov/capabilities/vaps/sondeadjust) for more information about this vap." - ] - }, - { - "cell_type": "markdown", - "id": "97097763", - "metadata": {}, - "source": [ - "In this notebook, we demonstrate the workflow to explore ARM vap data (using sondeadjust as an example.) Value-added products (VAPs) are higher-order data products that have been analyzed and processed to ease scientists’ use of ARM data in atmospheric research and global climate models. \n", - "Here is the main content we will cover." - ] - }, - { - "cell_type": "markdown", - "id": "eddec40f", - "metadata": {}, - "source": [ - "# Table of Content\n", - "## Access the data\n", - "* How to retrieve the data\n", - "* Data path and file name conventions\n", - "* Load data\n", - "## Explore the data\n", - "* NetCDF Data structure\n", - "* Xarray essentials\n", - "* Xarray Variable\n", - "## Plot the data" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "460fd89f-e034-452c-b837-f65c5958264f", - "metadata": {}, - "outputs": [], - "source": [ - "import ipywidgets as widgets\n", - "\n", - "import matplotlib.pyplot as plt\n", - "import numpy as np\n", - "import pandas as pd\n", - "import os\n", - "from datetime import datetime\n", - "\n", - "import act\n", - "import xarray as xr\n", - "\n", - "import random\n", - "\n", - "import glob\n", - "\n" - ] - }, - { - "cell_type": "markdown", - "id": "2372d5be", - "metadata": {}, - "source": [ - "## Access the data" - ] - }, - { - "cell_type": "markdown", - "id": "5b0d9684", - "metadata": {}, - "source": [ - "### How to retrieve the data\n", - "We assume the path \"/data/archive\" is available where you are running this notebook. You can use `os.path.exists(\"/data/archive\")` to verify if the path exists at your machine.\n", - "\n", - "\n", - "### Data path and file name conventions\n", - "There several common terminologies regarding ARM data, for example, data-stream-name, data-level, site, facility, instrument, etc. (For more details, please see [ARM Data File Standards Version 1.2](https://www.google.com/search?q=arm+datastream+facility+cite+definition&rlz=1C1GCEJ_enUS1029US1029&ei=hb41ZICrBPukqtsPvMODIA&ved=0ahUKEwjAgeiS0aL-AhV7kmoFHbzhAAQQ4dUDCBA&uact=5&oq=arm+datastream+facility+cite+definition&gs_lcp=Cgxnd3Mtd2l6LXNlcnAQAzIFCAAQogQyBQgAEKIEMgUIABCiBDIFCAAQogQyBQgAEKIEOgoIABBHENYEELADOgoIIRCgARDDBBAKSgQIQRgAUJ4DWIoOYO0PaAFwAXgAgAGMAYgBuQmSAQMyLjmYAQCgAQHIAQjAAQE&sclient=gws-wiz-serp).) \n", - "\n", - "For example, this notebook is called `sondeadjust.c1`, where `sondeadjust` is the \"datastream name\", and the `{process.ds_class_level}}` is the \"data level\".\n", - "\n", - "This datastream also contains site `nsa` and facility `C1`. (Note: individual datastream might have multiple site-facility pairs.)\n", - "In such a case, the data of this data-stream is stored at `/data/archive/nsa/nsasondeadjustC1.c1`, which is in the format of `//.`. We can use the following method to assign the data-stream directory `datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )`\n", - "\n", - "The data files under datastream_dir also follows naming conventions. But once reach the datastream_dir level, the most import file naming convention to differentiate the files is \"yyyyMMdd.hhmmss\", which comes handy to filter out files based on datetime. For example, we can use `glob.glob(f'{datastream_dir}/*.200709*.*')` to filter files in 2007 September.\n", - "\n", - "(Note: refer to the https://adc.arm.gov/discovery/#/ and https://adc.arm.gov/solr8/metadata/select API to explore ARM datastream and assoicated available sites and facilities\n", - "\n", - "Please see the following examples in action" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "d7e9eb85", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "True" - ] - }, - "execution_count": 2, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Verify if DATA_DIR path exists\n", - "DATA_DIR = \"/data/archive\"\n", - "os.path.exists(DATA_DIR)" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "586993fd", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "/data/archive/nsa/nsasondeadjustC1.c1\n", - "True\n" - ] - } - ], - "source": [ - "# Speicify datastream_dir following the path conventions and check its existence\n", - "DATASTREAM_NAME = \"sondeadjust\"\n", - "DATA_LEVEL = \"c1\"\n", - "site = \"nsa\"\n", - "facility = \"C1\"\n", - "datastream_dir = os.path.join(DATA_DIR, site, site + DATASTREAM_NAME + facility + '.' + DATA_LEVEL )\n", - "print(datastream_dir)\n", - "print(os.path.exists(datastream_dir))\n" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "0742f7c1", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "['nsasondeadjustC1.c1.20110710.054600.cdf',\n", - " 'nsasondeadjustC1.c1.20090319.051600.cdf',\n", - " 'nsasondeadjustC1.c1.20080508.052600.cdf',\n", - " 'nsasondeadjustC1.c1.20070816.181100.cdf',\n", - " 'nsasondeadjustC1.c1.20110923.174200.cdf']" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# bonus: list 5 (random) files under datastream_dir\n", - "files = os.listdir(datastream_dir)\n", - "files[:5]" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "39b98a36", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20080601.053500.cdf'" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# bonus: get most recent file\n", - "list_of_files = glob.glob(f\"{datastream_dir}/*\") # * means all if need specific format then *.csv\n", - "latest_file = max(list_of_files, key=os.path.getctime)\n", - "latest_file" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "902d514e", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "['nsasondeadjustC1.c1.20020428.184800.cdf', 'nsasondeadjustC1.c1.20020428.220500.cdf', 'nsasondeadjustC1.c1.20020428.235900.cdf', 'nsasondeadjustC1.c1.20020429.013100.cdf', 'nsasondeadjustC1.c1.20020429.182500.cdf']\n", - "['nsasondeadjustC1.c1.20120716.173000.cdf', 'nsasondeadjustC1.c1.20120716.053000.cdf', 'nsasondeadjustC1.c1.20120715.214900.cdf', 'nsasondeadjustC1.c1.20120715.173000.cdf', 'nsasondeadjustC1.c1.20120715.053000.cdf']\n" - ] - } - ], - "source": [ - "# bonus sort datastream files based on datetime\n", - "files = os.listdir(datastream_dir)\n", - "file_sorted = files.copy()\n", - "file_sorted.sort() \n", - "print(file_sorted[:5])\n", - "\n", - "# to reverse\n", - "file_sorted_reverse = files.copy() \n", - "file_sorted_reverse.sort(reverse=True)\n", - "print(file_sorted_reverse[:5])" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "id": "ec5923b2", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "['/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070921.173300.cdf',\n", - " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070929.190700.cdf',\n", - " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070925.201400.cdf',\n", - " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070928.174800.cdf',\n", - " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070930.172800.cdf',\n", - " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070905.052400.cdf',\n", - " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070927.052700.cdf',\n", - " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070920.165900.cdf',\n", - " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070929.053300.cdf',\n", - " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070909.053200.cdf',\n", - " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070904.204700.cdf',\n", - " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070913.210100.cdf',\n", - " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070925.052400.cdf',\n", - " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070907.173500.cdf',\n", - " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070919.053400.cdf',\n", - " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070901.172500.cdf',\n", - " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070919.174900.cdf',\n", - " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070903.210800.cdf',\n", - " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070913.180600.cdf',\n", - " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070914.204200.cdf',\n", - " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070916.052500.cdf',\n", - " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070917.213100.cdf',\n", - " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070904.052100.cdf',\n", - " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070915.172400.cdf',\n", - " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070923.172500.cdf',\n", - " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070903.173300.cdf',\n", - " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070928.205300.cdf',\n", - " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070905.174000.cdf',\n", - " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070926.213000.cdf',\n", - " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070918.052700.cdf',\n", - " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070914.173800.cdf',\n", - " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070914.053000.cdf',\n", - " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070918.205800.cdf',\n", - " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070911.052500.cdf',\n", - " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070917.180300.cdf',\n", - " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070904.174000.cdf',\n", - " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070901.052400.cdf',\n", - " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070918.181600.cdf',\n", - " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070911.174500.cdf',\n", - " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070907.053800.cdf',\n", - " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070910.213600.cdf',\n", - " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070902.172900.cdf',\n", - " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070924.173400.cdf',\n", - " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070910.052900.cdf',\n", - " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070906.220400.cdf',\n", - " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070920.053400.cdf',\n", - " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070926.173600.cdf',\n", - " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070906.053200.cdf',\n", - " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070923.052600.cdf',\n", - " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070911.221800.cdf',\n", - " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070917.052500.cdf',\n", - " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070927.180200.cdf',\n", - " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070907.213000.cdf',\n", - " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070910.173600.cdf',\n", - " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070912.212300.cdf',\n", - " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070912.052600.cdf',\n", - " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070922.172900.cdf',\n", - " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070925.180300.cdf',\n", - " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070909.172700.cdf',\n", - " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070921.052700.cdf',\n", - " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070928.053300.cdf',\n", - " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070903.052400.cdf',\n", - " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070902.052300.cdf',\n", - " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070930.052600.cdf',\n", - " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070912.173700.cdf',\n", - " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070915.052900.cdf',\n", - " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070927.211200.cdf',\n", - " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070916.173200.cdf',\n", - " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070913.053500.cdf',\n", - " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070922.061100.cdf',\n", - " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070908.052500.cdf',\n", - " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070905.210700.cdf',\n", - " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070906.175700.cdf',\n", - " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070908.175400.cdf',\n", - " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070924.052900.cdf',\n", - " '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20070922.053900.cdf']" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# bonus: pattern matching\n", - "# filter the 200709** files under datastream_dir\n", - "files_filter = glob.glob(f'{datastream_dir}/*.200709*.*')\n", - "files_filter" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "id": "c4a4aa18", - "metadata": {}, - "outputs": [], - "source": [ - "# bonus: use armnotebook_utils.file_filter (TODO) to filter files based on datastream info \n" - ] - }, - { - "cell_type": "markdown", - "id": "cfeb9efc", - "metadata": {}, - "source": [ - "### Load data\n", - "The arm data is stored in [NetCDF](https://en.wikipedia.org/wiki/NetCDF#:~:text=%22NetCDF%20(network%20Common%20Data%20Form,format%20for%20representing%20scientific%20data.) format. We can use xarray's `open_dataset` method to load single file, or `open_mfdataset` to open multiple files. (Note: the latter will still return a single xarray dataset object by combining multiple files.)\n", - "\n", - "Note: open_dataset keeps the file handle open and lazy loads its contents. All parameters are passed directly to open_dataset. It is a preferable method over load_dataset for memory-effiency. (more details, please see [xarray.open_dataset](https://docs.xarray.dev/en/stable/generated/xarray.open_dataset.html))\n", - "\n", - "See the following example in action" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "id": "a440a329", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20080601.053500.cdf\n" - ] - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "
<xarray.Dataset>\n",
-       "Dimensions:       (time: 2975)\n",
-       "Coordinates:\n",
-       "  * time          (time) datetime64[ns] 2008-06-01T05:35:00 ... 2008-06-01T07...\n",
-       "Data variables: (12/35)\n",
-       "    base_time     datetime64[ns] ...\n",
-       "    time_offset   (time) datetime64[ns] ...\n",
-       "    qc_time       (time) int32 ...\n",
-       "    pres          (time) float32 ...\n",
-       "    qc_pres       (time) int32 ...\n",
-       "    tdry          (time) float32 ...\n",
-       "    ...            ...\n",
-       "    qc_rh_scaled  (time) int32 ...\n",
-       "    dp_scaled     (time) float32 ...\n",
-       "    qc_dp_scaled  (time) int32 ...\n",
-       "    lat           (time) float32 ...\n",
-       "    lon           (time) float32 ...\n",
-       "    alt           (time) float32 ...\n",
-       "Attributes: (12/16)\n",
-       "    process_version:                $State: vap-sonde_adjust-8.0-0.sol5_10$\n",
-       "    command_line:                   sonde_adjust -d 20080601 -f nsaC1 -a 0\n",
-       "    site_id:                        nsa\n",
-       "    facility_id:                    C1: Barrow, Alaska\n",
-       "    reference1:                     Wang et.al. 2002. "Corrections of Humidit...\n",
-       "    reference2:                     Miloshevich et.al. 2004. "Development and...\n",
-       "    ...                             ...\n",
-       "    station_elevation:              8 m MSL\n",
-       "    input_datastreams_description:  A string consisting of the datastream(s),...\n",
-       "    input_datastreams_num:          6\n",
-       "    input_datastreams:              nsasondewnpnC1.b1 : 5.190000 : 20080601.0...\n",
-       "    zeb_platform:                   nsasondeadjustC1.c1\n",
-       "    history:                        created by user gervais on machine emeral...
" - ], - "text/plain": [ - "\n", - "Dimensions: (time: 2975)\n", - "Coordinates:\n", - " * time (time) datetime64[ns] 2008-06-01T05:35:00 ... 2008-06-01T07...\n", - "Data variables: (12/35)\n", - " base_time datetime64[ns] ...\n", - " time_offset (time) datetime64[ns] ...\n", - " qc_time (time) int32 ...\n", - " pres (time) float32 ...\n", - " qc_pres (time) int32 ...\n", - " tdry (time) float32 ...\n", - " ... ...\n", - " qc_rh_scaled (time) int32 ...\n", - " dp_scaled (time) float32 ...\n", - " qc_dp_scaled (time) int32 ...\n", - " lat (time) float32 ...\n", - " lon (time) float32 ...\n", - " alt (time) float32 ...\n", - "Attributes: (12/16)\n", - " process_version: $State: vap-sonde_adjust-8.0-0.sol5_10$\n", - " command_line: sonde_adjust -d 20080601 -f nsaC1 -a 0\n", - " site_id: nsa\n", - " facility_id: C1: Barrow, Alaska\n", - " reference1: Wang et.al. 2002. \"Corrections of Humidit...\n", - " reference2: Miloshevich et.al. 2004. \"Development and...\n", - " ... ...\n", - " station_elevation: 8 m MSL\n", - " input_datastreams_description: A string consisting of the datastream(s),...\n", - " input_datastreams_num: 6\n", - " input_datastreams: nsasondewnpnC1.b1 : 5.190000 : 20080601.0...\n", - " zeb_platform: nsasondeadjustC1.c1\n", - " history: created by user gervais on machine emeral..." - ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# open single file\n", - "full_path = latest_file\n", - "print(full_path)\n", - "ds_single = xr.open_dataset(full_path)\n", - "ds_single" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "id": "b0143a3d", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "Dimensions: (time: 2975)\n", - "Coordinates:\n", - " * time (time) datetime64[ns] 2008-06-01T05:35:00 ... 2008-06-01T07...\n", - "Data variables: (12/35)\n", - " base_time datetime64[ns] ...\n", - " time_offset (time) datetime64[ns] ...\n", - " qc_time (time) int32 ...\n", - " pres (time) float32 ...\n", - " qc_pres (time) int32 ...\n", - " tdry (time) float32 ...\n", - " ... ...\n", - " qc_rh_scaled (time) int32 ...\n", - " dp_scaled (time) float32 ...\n", - " qc_dp_scaled (time) int32 ...\n", - " lat (time) float32 ...\n", - " lon (time) float32 ...\n", - " alt (time) float32 ...\n", - "Attributes: (12/16)\n", - " process_version: $State: vap-sonde_adjust-8.0-0.sol5_10$\n", - " command_line: sonde_adjust -d 20080601 -f nsaC1 -a 0\n", - " site_id: nsa\n", - " facility_id: C1: Barrow, Alaska\n", - " reference1: Wang et.al. 2002. \"Corrections of Humidit...\n", - " reference2: Miloshevich et.al. 2004. \"Development and...\n", - " ... ...\n", - " station_elevation: 8 m MSL\n", - " input_datastreams_description: A string consisting of the datastream(s),...\n", - " input_datastreams_num: 6\n", - " input_datastreams: nsasondewnpnC1.b1 : 5.190000 : 20080601.0...\n", - " zeb_platform: nsasondeadjustC1.c1\n", - " history: created by user gervais on machine emeral...\n", - "\n" - ] - } - ], - "source": [ - "print(ds_single)\n", - "print(type(ds_single))" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "id": "1c0f8939", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "['/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20120716.173000.cdf', '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20120716.053000.cdf', '/data/archive/nsa/nsasondeadjustC1.c1/nsasondeadjustC1.c1.20120715.214900.cdf']\n", - "\n", - "\n", - "Dimensions: (time: 7872)\n", - "Coordinates:\n", - " * time (time) datetime64[ns] 2012-07-15T21:49:00 ... 2012-07-16T18...\n", - "Data variables: (12/35)\n", - " base_time (time) datetime64[ns] 2012-07-15T21:49:00 ... 2012-07-16T17...\n", - " time_offset (time) datetime64[ns] dask.array\n", - " qc_time (time) int32 dask.array\n", - " pres (time) float32 dask.array\n", - " qc_pres (time) int32 dask.array\n", - " tdry (time) float32 dask.array\n", - " ... ...\n", - " qc_rh_scaled (time) int32 dask.array\n", - " dp_scaled (time) float32 dask.array\n", - " qc_dp_scaled (time) int32 dask.array\n", - " lat (time) float32 dask.array\n", - " lon (time) float32 dask.array\n", - " alt (time) float32 dask.array\n", - "Attributes: (12/16)\n", - " process_version: $State: vap-sonde_adjust-8.0-0.sol5_10$\n", - " command_line: sonde_adjust -d 20120715 -f nsaC1 -a 0\n", - " site_id: nsa\n", - " facility_id: C1: Barrow, Alaska\n", - " reference1: Wang et.al. 2002. \"Corrections of Humidit...\n", - " reference2: Miloshevich et.al. 2004. \"Development and...\n", - " ... ...\n", - " station_elevation: 8 m MSL\n", - " input_datastreams_description: A string consisting of the datastream(s),...\n", - " input_datastreams_num: 6\n", - " input_datastreams: nsasondewnpnC1.b1 : 10.800000 : 20120715....\n", - " zeb_platform: nsasondeadjustC1.c1\n", - " history: created by user gervais on machine emeral...\n" - ] - } - ], - "source": [ - "# open multiple files\n", - "n_files = 3\n", - "full_paths = [os.path.join(datastream_dir, f_path) for f_path in file_sorted_reverse[:n_files]]\n", - "print(full_paths)\n", - "try: # Note: sometimes multiple files cannot be merged, so we used try except here.\n", - " ds_mutiple = xr.open_mfdataset(full_paths)\n", - " # ds_mutiple\n", - " print(type(ds_mutiple))\n", - " print(ds_mutiple)\n", - "except Exception as e:\n", - " print(e)" - ] - }, - { - "cell_type": "markdown", - "id": "3fa59943", - "metadata": {}, - "source": [ - "## Explore the data" - ] - }, - { - "cell_type": "markdown", - "id": "571f69b9", - "metadata": {}, - "source": [ - "### NetCDF Data structure \n", - "\n", - "\n", - "(If you are confident with NetCDF basics and xarray essentials, feel free to skip this session.)\n", - "Before we dive into data exploration, there are some eseential concpets we should be familiar with (shown below): \n", - "* Dataset\n", - "* Data array\n", - "* Variable\n", - "* Dimenssion\n", - "* Coordinate\n", - "* Data Type\n", - "* Meta Data (Attributes)\n", - "\n", - "We will not go into details about NetCDF basics and here are some references you might find helpful\n", - "* [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#)\n", - "* [Network Common Data Form (NetCDF)](https://www.unidata.ucar.edu/software/netcdf/) and [A Brief History of (netCDF) Time](https://www.unidata.ucar.edu/software/netcdf/time/recs.html)\n", - "\n", - "\n", - "\n", - "### Xarray essentials\n", - "Earlier we introduced the xarray pacakge and used `open_dataset` and `open_mfdataset` to retrieve NetCDF data file as an xarray Dataset object. Recall that you can review the data in a notebook by using `print(ds)` or `ds`. \n", - "\n", - "Using xarray to retrieve the aforementioned NetCDF basics is straightforwared. In general,\n", - "* Dataset: `ds`\n", - "* Data array: `ds.variables`\n", - "* Variable: `ds.ds.variables`\n", - "* Dimenssion: `ds.dims`\n", - "* Coordinate: `ds.coords`\n", - "* Data Type: `type`\n", - "* Meta Data (Attributes)\n", - "\n", - "Also, here are some references if you are new to xarray\n", - "* [Xarray in 45 minutes](https://tutorial.xarray.dev/overview/xarray-in-45-min.html)\n", - "* [Handling NetCDF Files using XArray for Absolute Beginners](https://towardsdatascience.com/handling-netcdf-files-using-xarray-for-absolute-beginners-111a8ab4463f)\n", - "\n", - "Try the following commands in action\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "id": "b3eac323", - "metadata": {}, - "outputs": [], - "source": [ - "ds = ds_single" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "id": "77ecf85d", - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "
<xarray.Dataset>\n",
-       "Dimensions:       (time: 2975)\n",
-       "Coordinates:\n",
-       "  * time          (time) datetime64[ns] 2008-06-01T05:35:00 ... 2008-06-01T07...\n",
-       "Data variables: (12/35)\n",
-       "    base_time     datetime64[ns] ...\n",
-       "    time_offset   (time) datetime64[ns] ...\n",
-       "    qc_time       (time) int32 ...\n",
-       "    pres          (time) float32 ...\n",
-       "    qc_pres       (time) int32 ...\n",
-       "    tdry          (time) float32 ...\n",
-       "    ...            ...\n",
-       "    qc_rh_scaled  (time) int32 ...\n",
-       "    dp_scaled     (time) float32 ...\n",
-       "    qc_dp_scaled  (time) int32 ...\n",
-       "    lat           (time) float32 ...\n",
-       "    lon           (time) float32 ...\n",
-       "    alt           (time) float32 ...\n",
-       "Attributes: (12/16)\n",
-       "    process_version:                $State: vap-sonde_adjust-8.0-0.sol5_10$\n",
-       "    command_line:                   sonde_adjust -d 20080601 -f nsaC1 -a 0\n",
-       "    site_id:                        nsa\n",
-       "    facility_id:                    C1: Barrow, Alaska\n",
-       "    reference1:                     Wang et.al. 2002. "Corrections of Humidit...\n",
-       "    reference2:                     Miloshevich et.al. 2004. "Development and...\n",
-       "    ...                             ...\n",
-       "    station_elevation:              8 m MSL\n",
-       "    input_datastreams_description:  A string consisting of the datastream(s),...\n",
-       "    input_datastreams_num:          6\n",
-       "    input_datastreams:              nsasondewnpnC1.b1 : 5.190000 : 20080601.0...\n",
-       "    zeb_platform:                   nsasondeadjustC1.c1\n",
-       "    history:                        created by user gervais on machine emeral...
" - ], - "text/plain": [ - "\n", - "Dimensions: (time: 2975)\n", - "Coordinates:\n", - " * time (time) datetime64[ns] 2008-06-01T05:35:00 ... 2008-06-01T07...\n", - "Data variables: (12/35)\n", - " base_time datetime64[ns] ...\n", - " time_offset (time) datetime64[ns] ...\n", - " qc_time (time) int32 ...\n", - " pres (time) float32 ...\n", - " qc_pres (time) int32 ...\n", - " tdry (time) float32 ...\n", - " ... ...\n", - " qc_rh_scaled (time) int32 ...\n", - " dp_scaled (time) float32 ...\n", - " qc_dp_scaled (time) int32 ...\n", - " lat (time) float32 ...\n", - " lon (time) float32 ...\n", - " alt (time) float32 ...\n", - "Attributes: (12/16)\n", - " process_version: $State: vap-sonde_adjust-8.0-0.sol5_10$\n", - " command_line: sonde_adjust -d 20080601 -f nsaC1 -a 0\n", - " site_id: nsa\n", - " facility_id: C1: Barrow, Alaska\n", - " reference1: Wang et.al. 2002. \"Corrections of Humidit...\n", - " reference2: Miloshevich et.al. 2004. \"Development and...\n", - " ... ...\n", - " station_elevation: 8 m MSL\n", - " input_datastreams_description: A string consisting of the datastream(s),...\n", - " input_datastreams_num: 6\n", - " input_datastreams: nsasondewnpnC1.b1 : 5.190000 : 20080601.0...\n", - " zeb_platform: nsasondeadjustC1.c1\n", - " history: created by user gervais on machine emeral..." - ] - }, - "execution_count": 13, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Dataset \n", - "ds" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "id": "25e7de09", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "Frozen({'base_time': \n", - "[1 values with dtype=datetime64[ns]]\n", - "Attributes:\n", - " string: 1-Jun-2008,5:35:00 GMT\n", - " long_name: Base time in Epoch, 'time_offset': \n", - "[2975 values with dtype=datetime64[ns]]\n", - "Attributes:\n", - " long_name: Time offset from base_time, 'time': \n", - "array(['2008-06-01T05:35:00.000000000', '2008-06-01T05:35:02.000000000',\n", - " '2008-06-01T05:35:04.000000000', ..., '2008-06-01T07:14:04.000000000',\n", - " '2008-06-01T07:14:06.000000000', '2008-06-01T07:14:08.000000000'],\n", - " dtype='datetime64[ns]')\n", - "Attributes:\n", - " long_name: Time offset from midnight, 'qc_time': \n", - "[2975 values with dtype=int32]\n", - "Attributes: (12/13)\n", - " long_name: Quality check results on field: Time offset from mi...\n", - " units: unitless\n", - " description: This field contains bit packed values which should ...\n", - " bit_1_description: Delta time between current and previous samples is ...\n", - " bit_1_assessment: Bad\n", - " bit_2_description: Delta time between current and previous samples is ...\n", - " ... ...\n", - " bit_3_description: Delta time between current and previous samples is ...\n", - " bit_3_assessment: Bad\n", - " delta_t_lower_limit: 20.0\n", - " delta_t_upper_limit: 20.0\n", - " prior_sample_flag: 1\n", - " comment: If the 'prior_sample_flag' is set the first sample ..., 'pres': \n", - "[2975 values with dtype=float32]\n", - "Attributes:\n", - " long_name: Barometric pressure\n", - " units: hPa\n", - " valid_min: 0.0\n", - " valid_max: 1100.0, 'qc_pres': \n", - "[2975 values with dtype=int32]\n", - "Attributes:\n", - " long_name: Quality check results on field: Barometric pressure\n", - " units: unitless\n", - " description: This field contains bit packed values which should be...\n", - " bit_1_description: Value is less than the valid_min.\n", - " bit_1_assessment: Indeterminate\n", - " bit_2_description: Value is greater than the valid_max.\n", - " bit_2_assessment: Indeterminate\n", - " bit_3_description: Data value not available in input file, data value se...\n", - " bit_3_assessment: Bad, 'tdry': \n", - "[2975 values with dtype=float32]\n", - "Attributes:\n", - " long_name: Dry bulb temperature\n", - " units: C\n", - " valid_min: -80.0\n", - " valid_max: 50.0, 'qc_tdry': \n", - "[2975 values with dtype=int32]\n", - "Attributes:\n", - " long_name: Quality check results on field: Dry bulb temperature\n", - " units: unitless\n", - " description: This field contains bit packed values which should be...\n", - " bit_1_description: Value is less than the valid_min.\n", - " bit_1_assessment: Indeterminate\n", - " bit_2_description: Value is greater than the valid_max.\n", - " bit_2_assessment: Indeterminate\n", - " bit_3_description: Data value not available in input file, data value se...\n", - " bit_3_assessment: Bad, 'dp': \n", - "[2975 values with dtype=float32]\n", - "Attributes:\n", - " long_name: Dewpoint temperature\n", - " units: C\n", - " valid_min: -110.0\n", - " valid_max: 50.0, 'qc_dp': \n", - "[2975 values with dtype=int32]\n", - "Attributes:\n", - " long_name: Quality check results on field: Dewpoint temperature\n", - " units: unitless\n", - " description: This field contains bit packed values which should be...\n", - " bit_1_description: Value is less than the valid_min.\n", - " bit_1_assessment: Indeterminate\n", - " bit_2_description: Value is greater than the valid_max.\n", - " bit_2_assessment: Indeterminate\n", - " bit_3_description: Data value not available in input file, data value se...\n", - " bit_3_assessment: Bad, 'wspd': \n", - "[2975 values with dtype=float32]\n", - "Attributes:\n", - " long_name: Wind speed\n", - " units: m/s\n", - " valid_min: 0.0\n", - " valid_max: 100.0, 'qc_wspd': \n", - "[2975 values with dtype=int32]\n", - "Attributes:\n", - " long_name: Quality check results on field: Wind speed\n", - " units: unitless\n", - " description: This field contains bit packed values which should be...\n", - " bit_1_description: Value is less than the valid_min.\n", - " bit_1_assessment: Indeterminate\n", - " bit_2_description: Value is greater than the valid_max.\n", - " bit_2_assessment: Indeterminate\n", - " bit_3_description: Data value not available in input file, data value se...\n", - " bit_3_assessment: Bad, 'deg': \n", - "[2975 values with dtype=float32]\n", - "Attributes:\n", - " long_name: Wind direction\n", - " units: deg\n", - " valid_min: 0.0\n", - " valid_max: 360.0, 'qc_deg': \n", - "[2975 values with dtype=int32]\n", - "Attributes:\n", - " long_name: Quality check results on field: Wind direction\n", - " units: unitless\n", - " description: This field contains bit packed values which should be...\n", - " bit_1_description: Value is less than the valid_min.\n", - " bit_1_assessment: Indeterminate\n", - " bit_2_description: Value is greater than the valid_max.\n", - " bit_2_assessment: Indeterminate\n", - " bit_3_description: Data value not available in input file, data value se...\n", - " bit_3_assessment: Bad, 'rh': \n", - "[2975 values with dtype=float32]\n", - "Attributes:\n", - " long_name: Relative humidity\n", - " units: %\n", - " valid_min: 0.0\n", - " valid_max: 105.0, 'qc_rh': \n", - "[2975 values with dtype=int32]\n", - "Attributes:\n", - " long_name: Quality check results on field: Relative humidity\n", - " units: unitless\n", - " description: This field contains bit packed values which should be...\n", - " bit_1_description: Value is less than the valid_min.\n", - " bit_1_assessment: Indeterminate\n", - " bit_2_description: Value is greater than the valid_max.\n", - " bit_2_assessment: Indeterminate\n", - " bit_3_description: Data value not available in input file, data value se...\n", - " bit_3_assessment: Bad, 'u_wind': \n", - "[2975 values with dtype=float32]\n", - "Attributes:\n", - " long_name: Eastward wind component\n", - " units: m/s\n", - " calc: -1 * sin( deg ) * wspd\n", - " valid_min: -75.0\n", - " valid_max: 75.0, 'qc_u_wind': \n", - "[2975 values with dtype=int32]\n", - "Attributes:\n", - " long_name: Quality check results on field: Eastward wind component\n", - " units: unitless\n", - " description: This field contains bit packed values which should be...\n", - " bit_1_description: Value is less than the valid_min.\n", - " bit_1_assessment: Indeterminate\n", - " bit_2_description: Value is greater than the valid_max.\n", - " bit_2_assessment: Indeterminate\n", - " bit_3_description: Data value not available in input file, data value se...\n", - " bit_3_assessment: Bad, 'v_wind': \n", - "[2975 values with dtype=float32]\n", - "Attributes:\n", - " long_name: Northward wind component\n", - " units: m/s\n", - " calc: -1 * cos( deg ) * wspd\n", - " valid_min: -75.0\n", - " valid_max: 75.0, 'qc_v_wind': \n", - "[2975 values with dtype=int32]\n", - "Attributes:\n", - " long_name: Quality check results on field: Northward wind component\n", - " units: unitless\n", - " description: This field contains bit packed values which should be...\n", - " bit_1_description: Value is less than the valid_min.\n", - " bit_1_assessment: Indeterminate\n", - " bit_2_description: Value is greater than the valid_max.\n", - " bit_2_assessment: Indeterminate\n", - " bit_3_description: Data value not available in input file, data value se...\n", - " bit_3_assessment: Bad, 'wstat': \n", - "[2975 values with dtype=float32]\n", - "Attributes:\n", - " long_name: Wind status\n", - " units: unitless, 'asc': \n", - "[2975 values with dtype=float32]\n", - "Attributes:\n", - " long_name: Ascent rate\n", - " units: m/s\n", - " valid_min: -10.0\n", - " valid_max: 20.0, 'qc_asc': \n", - "[2975 values with dtype=int32]\n", - "Attributes:\n", - " long_name: Quality check results on field: Ascent rate\n", - " units: unitless\n", - " description: This field contains bit packed values which should be...\n", - " bit_1_description: Value is less than the valid_min.\n", - " bit_1_assessment: Indeterminate\n", - " bit_2_description: Value is greater than the valid_max.\n", - " bit_2_assessment: Indeterminate\n", - " bit_3_description: Data value not available in input file, data value se...\n", - " bit_3_assessment: Bad, 'rh_smooth': \n", - "[2975 values with dtype=float32]\n", - "Attributes:\n", - " long_name: Smoothed original relative humidity\n", - " units: %\n", - " valid_min: 0.0\n", - " valid_max: 100.0\n", - " note: Intermediate RH profile created by smoothing original RH sond..., 'qc_rh_smooth': \n", - "[2975 values with dtype=int32]\n", - "Attributes:\n", - " long_name: Quality check results on field: Smoothed original rel...\n", - " units: unitless\n", - " description: This field contains bit packed values which should be...\n", - " bit_1_description: Value is less than the valid_min.\n", - " bit_1_assessment: Indeterminate\n", - " bit_2_description: Value is greater than the valid_max.\n", - " bit_2_assessment: Indeterminate\n", - " bit_3_description: Data value not available in input file, data value se...\n", - " bit_3_assessment: Bad, 'rh_biased': \n", - "[2975 values with dtype=float32]\n", - "Attributes:\n", - " long_name: Dry bias corrected relative humidity\n", - " units: %\n", - " valid_min: 0.0\n", - " valid_max: 100.0\n", - " note1: Eliminates the dry bias as described in Wang 2002\n", - " note2: This field differs from the rh_smooth field for only the RS-8..., 'qc_rh_biased': \n", - "[2975 values with dtype=int32]\n", - "Attributes:\n", - " long_name: Quality check results on field: Dry bias corrected re...\n", - " units: unitless\n", - " description: This field contains bit packed values which should be...\n", - " bit_1_description: Value is less than the valid_min.\n", - " bit_1_assessment: Indeterminate\n", - " bit_2_description: Value is greater than the valid_max.\n", - " bit_2_assessment: Indeterminate\n", - " bit_3_description: Data value not available in input file, data value se...\n", - " bit_3_assessment: Bad, 'rh_adjust': \n", - "[2975 values with dtype=float32]\n", - "Attributes:\n", - " long_name: Final corrected ambient relative humidity\n", - " units: %\n", - " valid_min: 0.0\n", - " valid_max: 100.0\n", - " note: corrects for sensor time-lag (RS-80 sondes) and the solar war..., 'qc_rh_adjust': \n", - "[2975 values with dtype=int32]\n", - "Attributes:\n", - " long_name: Quality check results on field: Final corrected ambie...\n", - " units: unitless\n", - " description: This field contains bit packed values which should be...\n", - " bit_1_description: Value is less than the valid_min.\n", - " bit_1_assessment: Indeterminate\n", - " bit_2_description: Value is greater than the valid_max.\n", - " bit_2_assessment: Indeterminate\n", - " bit_3_description: Data value not available in input file, data value se...\n", - " bit_3_assessment: Bad, 'rh_scaled': \n", - "[2975 values with dtype=float32]\n", - "Attributes:\n", - " long_name: Scaled final corrected ambient relative humidity\n", - " units: %\n", - " valid_min: 0.0\n", - " valid_max: 100.0\n", - " note1: scale factor is the be_pwv from mwrret1liljclou datasteam\n", - " note2: when there is no mwr or when pwv < 0.8, values are -9999, 'qc_rh_scaled': \n", - "[2975 values with dtype=int32]\n", - "Attributes:\n", - " long_name: Quality check results on field: Scaled final correcte...\n", - " units: unitless\n", - " description: This field contains bit packed values which should be...\n", - " bit_1_description: Value is less than the valid_min.\n", - " bit_1_assessment: Indeterminate\n", - " bit_2_description: Value is greater than the valid_max.\n", - " bit_2_assessment: Indeterminate\n", - " bit_3_description: Data value not available in input file, data value se...\n", - " bit_3_assessment: Bad\n", - " bit_4_description: The value of the pwv from the mwr file used to scale ...\n", - " bit_4_assessment: Bad, 'dp_scaled': \n", - "[2975 values with dtype=float32]\n", - "Attributes:\n", - " long_name: Scaled dewpoint temperature\n", - " units: C\n", - " valid_min: -110.0\n", - " valid_max: 50.0\n", - " note1: scale factor is the be_pwv from mwrret1liljclou datastream\n", - " note2: when there is no mwr or when pwv < 0.8, values are -9999, 'qc_dp_scaled': \n", - "[2975 values with dtype=int32]\n", - "Attributes:\n", - " long_name: Quality check results on field: Scaled dewpoint tempe...\n", - " units: unitless\n", - " description: This field contains bit packed values which should be...\n", - " bit_1_description: Value is less than the valid_min.\n", - " bit_1_assessment: Indeterminate\n", - " bit_2_description: Value is greater than the valid_max.\n", - " bit_2_assessment: Indeterminate\n", - " bit_3_description: Data value not available in input file, data value se...\n", - " bit_3_assessment: Bad\n", - " bit_4_description: The value of the pwv from the mwr file used to scale ...\n", - " bit_4_assessment: Bad, 'lat': \n", - "[2975 values with dtype=float32]\n", - "Attributes:\n", - " long_name: North latitude\n", - " units: degree_N\n", - " valid_min: -90.0\n", - " valid_max: 90.0, 'lon': \n", - "[2975 values with dtype=float32]\n", - "Attributes:\n", - " long_name: East longitude\n", - " units: degree_E\n", - " valid_min: -180.0\n", - " valid_max: 180.0, 'alt': \n", - "[2975 values with dtype=float32]\n", - "Attributes:\n", - " long_name: Altitude above mean sea level\n", - " units: m})" - ] - }, - "execution_count": 14, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Data array, Variable\n", - "# Note: the info can be overwhelming. Do not attempt to grasp everything at the first glance.\n", - "ds.variables" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "id": "8c41b67e", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "Frozen({'time': 2975})" - ] - }, - "execution_count": 15, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# dimenssions\n", - "ds.dims" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "id": "156f1dfc", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "Coordinates:\n", - " * time (time) datetime64[ns] 2008-06-01T05:35:00 ... 2008-06-01T07:14:08" - ] - }, - "execution_count": 16, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# coordinates\n", - "ds.coords" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "id": "277d6064", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{'process_version': '$State: vap-sonde_adjust-8.0-0.sol5_10$',\n", - " 'command_line': 'sonde_adjust -d 20080601 -f nsaC1 -a 0',\n", - " 'site_id': 'nsa',\n", - " 'facility_id': 'C1: Barrow, Alaska',\n", - " 'reference1': 'Wang et.al. 2002. \"Corrections of Humidity Measurement Errors from the Vaisala RS80-Radiosonde -- Application to TOGA COARE Data.\" Journal of Atmospheric and Oceanic Technology',\n", - " 'reference2': 'Miloshevich et.al. 2004. \"Development and Validation of a Time-Lag Correction for Vaisala Radiosonde Humidity Measurement.\" Journal of Atmospheric and Oceanic Technology',\n", - " 'reference3': 'Miloshevich et.al. 2009. \"Accuracy Assessment and Correction of Vaisala RS92 Radiosonde Water Vapor Measurements.\" Journal of Geophysical Research--Atmospheres',\n", - " 'qc_standards_version': '1.0',\n", - " 'dod_version': '5.0',\n", - " 'sonde_serial_number': 'C3526394',\n", - " 'station_elevation': '8 m MSL',\n", - " 'input_datastreams_description': 'A string consisting of the datastream(s), datastream version(s), and datastream date (range).',\n", - " 'input_datastreams_num': 6,\n", - " 'input_datastreams': 'nsasondewnpnC1.b1 : 5.190000 : 20080601.053500-20080601.175900 ;\\nnsametC1.b1 : 4.100000 : 20080531.234500-20080602.000000 ;',\n", - " 'zeb_platform': 'nsasondeadjustC1.c1',\n", - " 'history': 'created by user gervais on machine emerald at 2-Sep-2014,22:07:00, using $State: zebra-zeblib-4.23-0.el5 $'}" - ] - }, - "execution_count": 17, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Meta Data (Attributes)\n", - "ds.attrs" - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "id": "d334681f", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "\n", - "\n", - "\n", - "\n" - ] - } - ], - "source": [ - "# type\n", - "print(type(ds))\n", - "print(type(ds.variables))\n", - "print(type(ds.dims))\n", - "print(type(ds.coords))\n", - "print(type(ds.attrs))" - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "id": "643399d6", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "['base_time',\n", - " 'time_offset',\n", - " 'time',\n", - " 'qc_time',\n", - " 'pres',\n", - " 'qc_pres',\n", - " 'tdry',\n", - " 'qc_tdry',\n", - " 'dp',\n", - " 'qc_dp',\n", - " 'wspd',\n", - " 'qc_wspd',\n", - " 'deg',\n", - " 'qc_deg',\n", - " 'rh',\n", - " 'qc_rh',\n", - " 'u_wind',\n", - " 'qc_u_wind',\n", - " 'v_wind',\n", - " 'qc_v_wind',\n", - " 'wstat',\n", - " 'asc',\n", - " 'qc_asc',\n", - " 'rh_smooth',\n", - " 'qc_rh_smooth',\n", - " 'rh_biased',\n", - " 'qc_rh_biased',\n", - " 'rh_adjust',\n", - " 'qc_rh_adjust',\n", - " 'rh_scaled',\n", - " 'qc_rh_scaled',\n", - " 'dp_scaled',\n", - " 'qc_dp_scaled',\n", - " 'lat',\n", - " 'lon',\n", - " 'alt']" - ] - }, - "execution_count": 19, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# bonus: retrieve variable names only\n", - "list(ds.variables)" - ] - }, - { - "cell_type": "markdown", - "id": "1cf36878", - "metadata": {}, - "source": [ - "#### Discussion: variable vs. coordinates vs. dimenssions. \n", - "From [Components of a NetCDF Dataset](https://iprc.soest.hawaii.edu/users/xfu/tool/guidef-7.html#):\n", - "\n", - "* Dimession: A dimension may be used to represent a real physical dimension, for example, time, latitude, longitude, or height. A dimension might also be used to index other quantities, for example station or model-run-number.\n", - "A netCDF dimension has both a name and a length. A dimension length is an arbitrary positive integer, except that one dimension in a netCDF dataset can have the length UNLIMITED.\n", - "\n", - "* Variables: Variables are used to store the bulk of the data in a netCDF dataset. A variable represents an array of values of the same type. A scalar value is treated as a 0-dimensional array. A variable has a name, a data type, and a shape described by its list of dimensions specified when the variable is created. A variable may also have associated attributes, which may be added, deleted or changed after the variable is created.\n", - "\n", - "* Coordinate (Variables): It is legal for a variable to have the same name as a dimension. Such variables have no special meaning to the netCDF library. However there is a convention that such variables should be treated in a special way by software using this library.\n", - "A variable with the same name as a dimension is called a coordinate variable. It typically defines a physical coordinate corresponding to that dimension.\n", - "\n", - "\n", - "Tips: By definition, coordinates and dimenssions are also variable. For example ds.time is a coordinate but it is also a special variable. But in practice, when we talk about variable, it implies regular/non-coordinate variable. We will use this convention for the remaining notebook. " - ] - }, - { - "cell_type": "markdown", - "id": "d47120f9", - "metadata": {}, - "source": [ - "### Xarray Variable\n", - "\n", - "\n", - "We can use `ds.variables` to access varialbes of a dataset. But many times viewing print-out the whole datasets can be overwhemling. Instead, we would work on the individual variable (also called Xarray data array.) using the `ds[var_name]` syntax, where `var_name` is the name of the variable. See the following examples in action.\n" - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "id": "5a57e136", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n" - ] - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "
<xarray.DataArray 'time' (time: 2975)>\n",
-       "array(['2008-06-01T05:35:00.000000000', '2008-06-01T05:35:02.000000000',\n",
-       "       '2008-06-01T05:35:04.000000000', ..., '2008-06-01T07:14:04.000000000',\n",
-       "       '2008-06-01T07:14:06.000000000', '2008-06-01T07:14:08.000000000'],\n",
-       "      dtype='datetime64[ns]')\n",
-       "Coordinates:\n",
-       "  * time     (time) datetime64[ns] 2008-06-01T05:35:00 ... 2008-06-01T07:14:08\n",
-       "Attributes:\n",
-       "    long_name:  Time offset from midnight
" - ], - "text/plain": [ - "\n", - "array(['2008-06-01T05:35:00.000000000', '2008-06-01T05:35:02.000000000',\n", - " '2008-06-01T05:35:04.000000000', ..., '2008-06-01T07:14:04.000000000',\n", - " '2008-06-01T07:14:06.000000000', '2008-06-01T07:14:08.000000000'],\n", - " dtype='datetime64[ns]')\n", - "Coordinates:\n", - " * time (time) datetime64[ns] 2008-06-01T05:35:00 ... 2008-06-01T07:14:08\n", - "Attributes:\n", - " long_name: Time offset from midnight" - ] - }, - "execution_count": 20, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "print(type(ds[\"time\"]))\n", - "ds[\"time\"]" - ] - }, - { - "cell_type": "markdown", - "id": "51ec643e", - "metadata": {}, - "source": [ - "#### Variable properties\n", - "For individual varible, the following properties are mostly used with the assocaited xarray method to retrieve them. (assuming var = ds[var_name])\n", - "* name: `var.name`\n", - "* data content: `var.data`\n", - "* attributes: `var.attrs`\n", - "* dimenstions: `var.dims`\n", - "* data type: `var.data.dtype`" - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "id": "a632a525", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "var.name: \n", - " time \n", - "\n", - "var.data: \n", - " ['2008-06-01T05:35:00.000000000' '2008-06-01T05:35:02.000000000'\n", - " '2008-06-01T05:35:04.000000000' ... '2008-06-01T07:14:04.000000000'\n", - " '2008-06-01T07:14:06.000000000' '2008-06-01T07:14:08.000000000'] \n", - "\n", - "var.attrs: \n", - " {'long_name': 'Time offset from midnight'} \n", - "\n", - "var.dims: \n", - " ('time',) \n", - "\n", - "var.data.dtype: \n", - " datetime64[ns] \n", - "\n" - ] - } - ], - "source": [ - "var_name = \"time\"\n", - "var = ds[var_name]\n", - "\n", - "print(\"var.name: \\n\", var.name, \"\\n\")\n", - "print(\"var.data: \\n\", var.data, \"\\n\")\n", - "print(\"var.attrs: \\n\", var.attrs, \"\\n\")\n", - "print(\"var.dims: \\n\", var.dims, \"\\n\")\n", - "print(\"var.data.dtype: \\n\", var.data.dtype, \"\\n\")\n" - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "id": "b256b07f", - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
var_namedimsis_dimn_dimattrsdtype
0base_time()False0{'string': '1-Jun-2008,5:35:00 GMT', 'long_nam...datetime64[ns]
1time_offset(time,)False1{'long_name': 'Time offset from base_time'}datetime64[ns]
2time(time,)True1{'long_name': 'Time offset from midnight'}datetime64[ns]
3qc_time(time,)False1{'long_name': 'Quality check results on field:...int32
4pres(time,)False1{'long_name': 'Barometric pressure', 'units': ...float32
5qc_pres(time,)False1{'long_name': 'Quality check results on field:...int32
6tdry(time,)False1{'long_name': 'Dry bulb temperature', 'units':...float32
7qc_tdry(time,)False1{'long_name': 'Quality check results on field:...int32
8dp(time,)False1{'long_name': 'Dewpoint temperature', 'units':...float32
9qc_dp(time,)False1{'long_name': 'Quality check results on field:...int32
10wspd(time,)False1{'long_name': 'Wind speed', 'units': 'm/s', 'v...float32
11qc_wspd(time,)False1{'long_name': 'Quality check results on field:...int32
12deg(time,)False1{'long_name': 'Wind direction', 'units': 'deg'...float32
13qc_deg(time,)False1{'long_name': 'Quality check results on field:...int32
14rh(time,)False1{'long_name': 'Relative humidity', 'units': '%...float32
15qc_rh(time,)False1{'long_name': 'Quality check results on field:...int32
16u_wind(time,)False1{'long_name': 'Eastward wind component', 'unit...float32
17qc_u_wind(time,)False1{'long_name': 'Quality check results on field:...int32
18v_wind(time,)False1{'long_name': 'Northward wind component', 'uni...float32
19qc_v_wind(time,)False1{'long_name': 'Quality check results on field:...int32
20wstat(time,)False1{'long_name': 'Wind status', 'units': 'unitless'}float32
21asc(time,)False1{'long_name': 'Ascent rate', 'units': 'm/s', '...float32
22qc_asc(time,)False1{'long_name': 'Quality check results on field:...int32
23rh_smooth(time,)False1{'long_name': 'Smoothed original relative humi...float32
24qc_rh_smooth(time,)False1{'long_name': 'Quality check results on field:...int32
25rh_biased(time,)False1{'long_name': 'Dry bias corrected relative hum...float32
26qc_rh_biased(time,)False1{'long_name': 'Quality check results on field:...int32
27rh_adjust(time,)False1{'long_name': 'Final corrected ambient relativ...float32
28qc_rh_adjust(time,)False1{'long_name': 'Quality check results on field:...int32
29rh_scaled(time,)False1{'long_name': 'Scaled final corrected ambient ...float32
30qc_rh_scaled(time,)False1{'long_name': 'Quality check results on field:...int32
31dp_scaled(time,)False1{'long_name': 'Scaled dewpoint temperature', '...float32
32qc_dp_scaled(time,)False1{'long_name': 'Quality check results on field:...int32
33lat(time,)False1{'long_name': 'North latitude', 'units': 'degr...float32
34lon(time,)False1{'long_name': 'East longitude', 'units': 'degr...float32
35alt(time,)False1{'long_name': 'Altitude above mean sea level',...float32
\n", - "
" - ], - "text/plain": [ - " var_name dims is_dim n_dim \\\n", - "0 base_time () False 0 \n", - "1 time_offset (time,) False 1 \n", - "2 time (time,) True 1 \n", - "3 qc_time (time,) False 1 \n", - "4 pres (time,) False 1 \n", - "5 qc_pres (time,) False 1 \n", - "6 tdry (time,) False 1 \n", - "7 qc_tdry (time,) False 1 \n", - "8 dp (time,) False 1 \n", - "9 qc_dp (time,) False 1 \n", - "10 wspd (time,) False 1 \n", - "11 qc_wspd (time,) False 1 \n", - "12 deg (time,) False 1 \n", - "13 qc_deg (time,) False 1 \n", - "14 rh (time,) False 1 \n", - "15 qc_rh (time,) False 1 \n", - "16 u_wind (time,) False 1 \n", - "17 qc_u_wind (time,) False 1 \n", - "18 v_wind (time,) False 1 \n", - "19 qc_v_wind (time,) False 1 \n", - "20 wstat (time,) False 1 \n", - "21 asc (time,) False 1 \n", - "22 qc_asc (time,) False 1 \n", - "23 rh_smooth (time,) False 1 \n", - "24 qc_rh_smooth (time,) False 1 \n", - "25 rh_biased (time,) False 1 \n", - "26 qc_rh_biased (time,) False 1 \n", - "27 rh_adjust (time,) False 1 \n", - "28 qc_rh_adjust (time,) False 1 \n", - "29 rh_scaled (time,) False 1 \n", - "30 qc_rh_scaled (time,) False 1 \n", - "31 dp_scaled (time,) False 1 \n", - "32 qc_dp_scaled (time,) False 1 \n", - "33 lat (time,) False 1 \n", - "34 lon (time,) False 1 \n", - "35 alt (time,) False 1 \n", - "\n", - " attrs dtype \n", - "0 {'string': '1-Jun-2008,5:35:00 GMT', 'long_nam... datetime64[ns] \n", - "1 {'long_name': 'Time offset from base_time'} datetime64[ns] \n", - "2 {'long_name': 'Time offset from midnight'} datetime64[ns] \n", - "3 {'long_name': 'Quality check results on field:... int32 \n", - "4 {'long_name': 'Barometric pressure', 'units': ... float32 \n", - "5 {'long_name': 'Quality check results on field:... int32 \n", - "6 {'long_name': 'Dry bulb temperature', 'units':... float32 \n", - "7 {'long_name': 'Quality check results on field:... int32 \n", - "8 {'long_name': 'Dewpoint temperature', 'units':... float32 \n", - "9 {'long_name': 'Quality check results on field:... int32 \n", - "10 {'long_name': 'Wind speed', 'units': 'm/s', 'v... float32 \n", - "11 {'long_name': 'Quality check results on field:... int32 \n", - "12 {'long_name': 'Wind direction', 'units': 'deg'... float32 \n", - "13 {'long_name': 'Quality check results on field:... int32 \n", - "14 {'long_name': 'Relative humidity', 'units': '%... float32 \n", - "15 {'long_name': 'Quality check results on field:... int32 \n", - "16 {'long_name': 'Eastward wind component', 'unit... float32 \n", - "17 {'long_name': 'Quality check results on field:... int32 \n", - "18 {'long_name': 'Northward wind component', 'uni... float32 \n", - "19 {'long_name': 'Quality check results on field:... int32 \n", - "20 {'long_name': 'Wind status', 'units': 'unitless'} float32 \n", - "21 {'long_name': 'Ascent rate', 'units': 'm/s', '... float32 \n", - "22 {'long_name': 'Quality check results on field:... int32 \n", - "23 {'long_name': 'Smoothed original relative humi... float32 \n", - "24 {'long_name': 'Quality check results on field:... int32 \n", - "25 {'long_name': 'Dry bias corrected relative hum... float32 \n", - "26 {'long_name': 'Quality check results on field:... int32 \n", - "27 {'long_name': 'Final corrected ambient relativ... float32 \n", - "28 {'long_name': 'Quality check results on field:... int32 \n", - "29 {'long_name': 'Scaled final corrected ambient ... float32 \n", - "30 {'long_name': 'Quality check results on field:... int32 \n", - "31 {'long_name': 'Scaled dewpoint temperature', '... float32 \n", - "32 {'long_name': 'Quality check results on field:... int32 \n", - "33 {'long_name': 'North latitude', 'units': 'degr... float32 \n", - "34 {'long_name': 'East longitude', 'units': 'degr... float32 \n", - "35 {'long_name': 'Altitude above mean sea level',... float32 " - ] - }, - "execution_count": 22, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# bonus: indivdual variable info table (collect and display variable info as a tabular format)\n", - "df_info = pd.DataFrame()\n", - "df_info[\"var_name\"] = list(ds.variables)\n", - "df_info[\"dims\"] = df_info.var_name.apply(lambda x: ds[x].dims)\n", - "df_info[\"is_dim\"] = df_info.var_name.apply(lambda x: x in ds.dims)\n", - "df_info[\"n_dim\"] = df_info.var_name.apply(lambda x: len(ds[x].dims))\n", - "df_info[\"attrs\"] = df_info.var_name.apply(lambda x: ds[x].attrs)\n", - "df_info[\"dtype\"] = df_info.var_name.apply(lambda x: ds[x].data.dtype)\n", - "df_info" - ] - }, - { - "cell_type": "markdown", - "id": "e6a36b96", - "metadata": {}, - "source": [ - "### Data cleaning/Preprocessing (skipped)\n", - "Data cleaning and preprocessing is an important stage in the the data analysis pipeline. However it will be out of the scope of this notebook. For more information about data cleaning and preprocessing basics, here are some references. \n", - "* [Xarray Fundamentals](https://earth-env-data-science.github.io/lectures/xarray/xarray.html)\n", - "* [Xarray Tutorial — Pangeo Gallery documentation](http://gallery.pangeo.io/repos/pangeo-data/pangeo-tutorial-gallery/xarray.html)\n", - "* [Pythonic Data Cleaning With pandas and NumPy](https://realpython.com/python-data-cleaning-numpy-pandas/)\n", - "* [Pandas - Cleaning Data](https://www.w3schools.com/python/pandas/pandas_cleaning.asp)" - ] - }, - { - "cell_type": "markdown", - "id": "ae6e1a69", - "metadata": {}, - "source": [ - "### Plotting\n", - "Data visualization (or plotting) is another important data analysis topic and deserves its own discussion. Here in this notebook we will only demonstrate basic tool for simple data visualization tasks.\n", - "\n", - "Here are some reference you might find useful:\n", - "* xarray plotting: https://docs.xarray.dev/en/stable/user-guide/plotting.html\n", - "* Atmospheric Community Toolkit (ACT): https://arm-doe.github.io/ACT/index.html\n", - "\n", - "Note: this notebook is auto-generated using a template. It uses a general idea to select variable(s) to plot and is not customized to each indivdual notebook. Feel free to change the variables in intrrests, especially certain figure is failed to plot." - ] - }, - { - "cell_type": "markdown", - "id": "7e417fb2", - "metadata": {}, - "source": [ - "#### 1-dimenssional basic time series plot\n", - "\n", - "For the following plot we would like to find variables such that\n", - "* it has one and only one dimession\n", - "* \"time\" is its coordinate variable\n", - "* it is not a dimenssion itself,\n", - "* it is not a special variable with substrings within [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", - "\n", - "Please see the following example in action. (For more details about pandas filtering, please see the following references.)\n", - "* [pandas: multiple conditions while indexing data frame](https://stackoverflow.com/questions/22591174/pandas-multiple-conditions-while-indexing-data-frame-unexpected-behavior)\n", - "* [pandas.DataFrame.apply](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.apply.html)\n", - "* [How to test if a string contains one of the substrings in a list...](https://stackoverflow.com/questions/26577516/how-to-test-if-a-string-contains-one-of-the-substrings-in-a-list-in-pandas" - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "id": "30edac2d", - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
var_namedimsis_dimn_dimattrsdtype
4pres(time,)False1{'long_name': 'Barometric pressure', 'units': ...float32
6tdry(time,)False1{'long_name': 'Dry bulb temperature', 'units':...float32
8dp(time,)False1{'long_name': 'Dewpoint temperature', 'units':...float32
10wspd(time,)False1{'long_name': 'Wind speed', 'units': 'm/s', 'v...float32
12deg(time,)False1{'long_name': 'Wind direction', 'units': 'deg'...float32
14rh(time,)False1{'long_name': 'Relative humidity', 'units': '%...float32
16u_wind(time,)False1{'long_name': 'Eastward wind component', 'unit...float32
18v_wind(time,)False1{'long_name': 'Northward wind component', 'uni...float32
20wstat(time,)False1{'long_name': 'Wind status', 'units': 'unitless'}float32
21asc(time,)False1{'long_name': 'Ascent rate', 'units': 'm/s', '...float32
23rh_smooth(time,)False1{'long_name': 'Smoothed original relative humi...float32
25rh_biased(time,)False1{'long_name': 'Dry bias corrected relative hum...float32
27rh_adjust(time,)False1{'long_name': 'Final corrected ambient relativ...float32
29rh_scaled(time,)False1{'long_name': 'Scaled final corrected ambient ...float32
31dp_scaled(time,)False1{'long_name': 'Scaled dewpoint temperature', '...float32
\n", - "
" - ], - "text/plain": [ - " var_name dims is_dim n_dim \\\n", - "4 pres (time,) False 1 \n", - "6 tdry (time,) False 1 \n", - "8 dp (time,) False 1 \n", - "10 wspd (time,) False 1 \n", - "12 deg (time,) False 1 \n", - "14 rh (time,) False 1 \n", - "16 u_wind (time,) False 1 \n", - "18 v_wind (time,) False 1 \n", - "20 wstat (time,) False 1 \n", - "21 asc (time,) False 1 \n", - "23 rh_smooth (time,) False 1 \n", - "25 rh_biased (time,) False 1 \n", - "27 rh_adjust (time,) False 1 \n", - "29 rh_scaled (time,) False 1 \n", - "31 dp_scaled (time,) False 1 \n", - "\n", - " attrs dtype \n", - "4 {'long_name': 'Barometric pressure', 'units': ... float32 \n", - "6 {'long_name': 'Dry bulb temperature', 'units':... float32 \n", - "8 {'long_name': 'Dewpoint temperature', 'units':... float32 \n", - "10 {'long_name': 'Wind speed', 'units': 'm/s', 'v... float32 \n", - "12 {'long_name': 'Wind direction', 'units': 'deg'... float32 \n", - "14 {'long_name': 'Relative humidity', 'units': '%... float32 \n", - "16 {'long_name': 'Eastward wind component', 'unit... float32 \n", - "18 {'long_name': 'Northward wind component', 'uni... float32 \n", - "20 {'long_name': 'Wind status', 'units': 'unitless'} float32 \n", - "21 {'long_name': 'Ascent rate', 'units': 'm/s', '... float32 \n", - "23 {'long_name': 'Smoothed original relative humi... float32 \n", - "25 {'long_name': 'Dry bias corrected relative hum... float32 \n", - "27 {'long_name': 'Final corrected ambient relativ... float32 \n", - "29 {'long_name': 'Scaled final corrected ambient ... float32 \n", - "31 {'long_name': 'Scaled dewpoint temperature', '... float32 " - ] - }, - "execution_count": 23, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# (pandas query) filter n_dim==1, non-dimenssional, and not contains the following substrings\n", - "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", - "df_filter = df_info[(df_info.n_dim == 1) &\n", - " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", - " (df_info.is_dim==False) &\n", - " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", - " ]\n", - "df_filter" - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "id": "4777a659", - "metadata": {}, - "outputs": [], - "source": [ - "try:\n", - " var_1d = df_filter.var_name.values[0]\n", - " var_1d\n", - "except Exception as e:\n", - " print(e) " - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "id": "fb5ae985", - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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pVIplEqlaNq0qZg/f36t1qxPD2qPimPk7r/giouLxdy5c0WjRo3EwYMHhRD/TQjNy8sTK1euNOpj5EHtkZSUJIT4r4coMzNTZGZmauzXpk2bKk/0xqo6x4cQ5ncO+eijjyr9sjbnc8j92sNUziEMREZuzpw5QiaTibFjx2psv337tujdu7fo2LGjetuiRYuEr6+vsLGxEV27dhX//POPWLJkiZDL5eLatWu1XbrezJ07VygUCvH000+LixcvqkNiamqq6N27t+jQoYN63xkzZojw8HCxfv169babN2+KgIAAsXbtWiGE8XfzP6w97j5GhPjv8545c0YMHjxYPPnkk+LmzZti5MiR4q+//qr1+nXtUduj4r83b94UzZo1Ex999JHGdmOlTXuYyzlk1qxZwsrKSshkMvHbb78JIcqDTsV51dzOIQ9rD1M5hzAQGam//vpLeHt7iwYNGogtW7ZUel6lUomvv/5azJgxQ70uxo4dO0SvXr3Eb7/9pk7qqampIiQkRMyePbtW69eX//3vf6Jx48bijz/+ECUlJRrj+iqVSnz11VdixowZIjc3VwghRFxcnBg7dqxwc3MTf/zxh7h8+bJYvHixaNKkifpSWWNW3fYoLCys8vVffvmlsLW1FVZWVqJevXoiLi6utkrXi5q2h1KpFAsWLBCdO3cWly5dqq2y9aa67WEu55D169cLHx8f0aBBA/HXX3+Jbt26iSlTpqifv/u8ag7nEG3awxTOIQxERujgwYPC29tbdO/eXb0tLS1NFBYWaiwHX1BQUOm1FZPe7nb79m291FmblEqlSE1NFe3btxebNm0SQggRHx8vDh8+LBITE0VxcbEQQqhP7HdLTU0VI0eOFMHBwaJu3brC19dX/R7GqibtIUR51/aff/4pfH19Rd26dcXmzZtrrXZ9qEl7FBcXiwMHDogvv/xStGzZUgQGBopt27bVav26VpP2MNVzyPvvvy9sbGzE4sWL1duefPJJMWLECFFYWKju9aiYJ3Q3UzyH1KQ9hDDOcwgDkRFKTU0VU6ZMEZ07dxbXrl0T77//vggPDxft2rUTAwcOFBcuXKj0moqD1xRXRa34bHFxccLb21skJyeLuXPnCm9vb9G6dWsREBCg8dfr/doiNTVVREVF1V7hevKo7XF3t35hYaHo16+fiIiIqN3i9aCm7ZGVlSVWrFghevbs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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# Note: if failed, change to another variable to plot.\n", - "try:\n", - " ds[var_1d].plot()\n", - " plt.show()\n", - "except Exception as e:\n", - " print(e) " - ] - }, - { - "cell_type": "markdown", - "id": "9e41066e", - "metadata": {}, - "source": [ - "#### 2-dimenssional basic plot" - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "id": "09bd4adc", - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
var_namedimsis_dimn_dimattrsdtype
\n", - "
" - ], - "text/plain": [ - "Empty DataFrame\n", - "Columns: [var_name, dims, is_dim, n_dim, attrs, dtype]\n", - "Index: []" - ] - }, - "execution_count": 26, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# (pandas query) filter n_dim==2, non-dimenssional, and not contains the following substrings\n", - "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", - "df_filter_2 = df_info[(df_info.n_dim == 2) &\n", - " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", - " (df_info.is_dim==False) &\n", - " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", - " ]\n", - "df_filter_2" - ] - }, - { - "cell_type": "code", - "execution_count": 27, - "id": "2cdea1a9", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "index 0 is out of bounds for axis 0 with size 0\n" - ] - } - ], - "source": [ - "try:\n", - " var_2d = df_filter_2.var_name.values[0]\n", - " var_2d\n", - "except Exception as e:\n", - " print(e) " - ] - }, - { - "cell_type": "code", - "execution_count": 28, - "id": "f5f48879", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "name 'var_2d' is not defined\n" - ] - } - ], - "source": [ - "# Note: if failed, change to another variable to plot.\n", - "try:\n", - " print(ds[var_2d].dims)\n", - " # ds[var_2d].plot()\n", - "\n", - " # conventionally, use \"time\" as x-axis\n", - " ds[var_2d].plot(x=\"time\")\n", - " plt.show()\n", - "except Exception as e:\n", - " print(e) " - ] - }, - { - "cell_type": "markdown", - "id": "7554b2a6", - "metadata": {}, - "source": [ - "#### qc-plotting (optional)\n", - "\n", - "Note: act qc plotting has more strict requirement, one of them is the associated qc variable need to have \"flag_masks\" attributes. Which is added by using ds = act.io.armfiles.read_netcdf(files_list), then ds.clean.cleanup()" - ] - }, - { - "cell_type": "code", - "execution_count": 29, - "id": "71c2096f", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n" - ] - } - ], - "source": [ - "try:\n", - " ds_act = act.io.armfiles.read_netcdf(full_path)\n", - " print(type(ds_act))\n", - " ds_act.clean.cleanup()\n", - "\n", - " # or \n", - " # ds.clean.cleanup()\n", - "except Exception as e:\n", - " print(\"ERROR\", e)\n", - " ds_act = ds" - ] - }, - { - "cell_type": "code", - "execution_count": 30, - "id": "844f8505", - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
var_namedimsis_dimn_dimattrsdtype
4pres(time,)False1{'long_name': 'Barometric pressure', 'units': ...float32
6tdry(time,)False1{'long_name': 'Dry bulb temperature', 'units':...float32
8dp(time,)False1{'long_name': 'Dewpoint temperature', 'units':...float32
10wspd(time,)False1{'long_name': 'Wind speed', 'units': 'm/s', 'v...float32
12deg(time,)False1{'long_name': 'Wind direction', 'units': 'deg'...float32
14rh(time,)False1{'long_name': 'Relative humidity', 'units': '%...float32
16u_wind(time,)False1{'long_name': 'Eastward wind component', 'unit...float32
18v_wind(time,)False1{'long_name': 'Northward wind component', 'uni...float32
21asc(time,)False1{'long_name': 'Ascent rate', 'units': 'm/s', '...float32
23rh_smooth(time,)False1{'long_name': 'Smoothed original relative humi...float32
25rh_biased(time,)False1{'long_name': 'Dry bias corrected relative hum...float32
27rh_adjust(time,)False1{'long_name': 'Final corrected ambient relativ...float32
29rh_scaled(time,)False1{'long_name': 'Scaled final corrected ambient ...float32
31dp_scaled(time,)False1{'long_name': 'Scaled dewpoint temperature', '...float32
\n", - "
" - ], - "text/plain": [ - " var_name dims is_dim n_dim \\\n", - "4 pres (time,) False 1 \n", - "6 tdry (time,) False 1 \n", - "8 dp (time,) False 1 \n", - "10 wspd (time,) False 1 \n", - "12 deg (time,) False 1 \n", - "14 rh (time,) False 1 \n", - "16 u_wind (time,) False 1 \n", - "18 v_wind (time,) False 1 \n", - "21 asc (time,) False 1 \n", - "23 rh_smooth (time,) False 1 \n", - "25 rh_biased (time,) False 1 \n", - "27 rh_adjust (time,) False 1 \n", - "29 rh_scaled (time,) False 1 \n", - "31 dp_scaled (time,) False 1 \n", - "\n", - " attrs dtype \n", - "4 {'long_name': 'Barometric pressure', 'units': ... float32 \n", - "6 {'long_name': 'Dry bulb temperature', 'units':... float32 \n", - "8 {'long_name': 'Dewpoint temperature', 'units':... float32 \n", - "10 {'long_name': 'Wind speed', 'units': 'm/s', 'v... float32 \n", - "12 {'long_name': 'Wind direction', 'units': 'deg'... float32 \n", - "14 {'long_name': 'Relative humidity', 'units': '%... float32 \n", - "16 {'long_name': 'Eastward wind component', 'unit... float32 \n", - "18 {'long_name': 'Northward wind component', 'uni... float32 \n", - "21 {'long_name': 'Ascent rate', 'units': 'm/s', '... float32 \n", - "23 {'long_name': 'Smoothed original relative humi... float32 \n", - "25 {'long_name': 'Dry bias corrected relative hum... float32 \n", - "27 {'long_name': 'Final corrected ambient relativ... float32 \n", - "29 {'long_name': 'Scaled final corrected ambient ... float32 \n", - "31 {'long_name': 'Scaled dewpoint temperature', '... float32 " - ] - }, - "execution_count": 30, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Filter valid variables for ACT qc plotting\n", - "condition = (\"qc_\" + df_info.var_name).apply(lambda x: ds_act[x].attrs.get(\"flag_masks\") is not None \n", - " if x in list(ds.variables) else False)\n", - "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", - "df_filter_3 = df_info[(df_info.is_dim==False) &\n", - " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", - " (~df_info.var_name.str.contains('|'.join(exclude_substrings))) &\n", - " condition\n", - " ]\n", - "df_filter_3" - ] - }, - { - "cell_type": "code", - "execution_count": 31, - "id": "ecd38cc6", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "pres\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/home/kefeimo/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/act/utils/datetime_utils.py:136: FutureWarning: Unlike other reduction functions (e.g. `skew`, `kurtosis`), the default behavior of `mode` typically preserves the axis it acts along. In SciPy 1.11.0, this behavior will change: the default value of `keepdims` will become False, the `axis` over which the statistic is taken will be eliminated, and the value None will no longer be accepted. Set `keepdims` to True or False to avoid this warning.\n", - " mode = stats.mode(np.diff(time))\n" - ] - }, - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAzUAAANCCAYAAABfy+KVAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAA9hAAAPYQGoP6dpAACuSklEQVR4nOzdaXgUVfr38V+TPSGEPQuGgIgggsiisoigbIos6rgN6oAPKoobboyoI8EFFBGZwZ1BwN2/KOooIqCASoICgoIICrJKIgIhYc16nheVbtJJulMJnaQ7+X6uKxd01amqU3cqXXXXOXXKYYwxAgAAAIAAVae6KwAAAAAAJ4OkBgAAAEBAI6kBAAAAENBIagAAAAAENJIaAAAAAAGNpAYAAABAQCOpAQAAABDQSGoAAAAABDSSGgAAAAABjaQGACrZypUrddVVVyk+Pl6hoaGKj4/X1VdfrVWrVnlc5qefftKNN96oli1bKjw8XHXr1lXnzp01ZcoUHThwoAprf/KSk5PlcDjcpr344ouaM2dO9VToJH355Zfq2rWroqKi5HA49NFHH2nOnDlyOBzavn17uddXnmX79OmjPn36lHsbAFDTBVd3BQCgJpsxY4bGjh2rc889V1OmTFFSUpJ27typF154Qd26ddNLL72kW265xW2ZmTNnasyYMWrTpo0eeOABtWvXTrm5uVq9erVefvllpaamav78+dW0R77x4osvqnHjxho5cmR1V6VcjDG6+uqrdfrpp+uTTz5RVFSU2rRpo7y8PKWmpio+Pr66qwgAtRJJDQBUkhUrVmjs2LEaNGiQ5s+fr+DgE1+51157rS6//HKNGTNGnTp10jnnnCNJSk1N1W233ab+/fvro48+UlhYmGuZ/v3767777tPChQvLXZejR48qMjLy5HeqltuzZ48OHDigyy+/XH379nWb16RJk2qqVdXgGALgz+h+BgCVZPLkyXI4HHrppZfcEhpJCg4O1osvvugq5zRp0iQ5HA69+uqrbgmNU2hoqIYOHep1uyNHjlTdunW1fv16DRgwQNHR0a4L8JycHD3xxBNq27atwsLC1KRJE914443666+/3Nbx1VdfqU+fPmrUqJEiIiLUvHlz/e1vf9PRo0clScuWLZPD4dCyZcvcltu+fbscDofXrmUtWrTQzz//rOXLl8vhcMjhcKhFixaSpIKCAj3xxBNq06aNIiIiVL9+fZ111ln697//7XWfJWnnzp26/vrr1bRpU4WFhemMM87Qs88+q4KCghL1mzp1qqZNm6aWLVuqbt266t69u1auXOl1/cnJyTrllFMkSf/85z/d6u2pC9mSJUvUt29f1atXT5GRkerZs6e+/PLLMvfFGONq2QsPD1fnzp31+eefl7mcN87f2Ztvvql7771XcXFxioiIUO/evbV27Vq3slVxDAGAL9FSAwCVID8/X0uXLlXXrl1dF8LFJSYmqkuXLlqyZIkKCgpkjNFXX32lLl26KDEx8aS2n5OTo6FDh2r06NF68MEHlZeXp4KCAg0bNkzffPONxo0bpx49emjHjh2aMGGC+vTpo9WrVysiIkLbt2/XpZdeql69eum1115T/fr19ccff2jhwoXKyck56bv18+fP15VXXqmYmBhXYudM4KZMmaLk5GQ98sgjuuCCC5Sbm6tNmzbp4MGDXtf5119/qUePHsrJydHjjz+uFi1a6NNPP9X999+vrVu3urbj9MILL6ht27aaPn26JOlf//qXBg0apG3btikmJqbUbdx0003q2LGjrrjiCt15550aPnx4qYmn05tvvql//OMfGjZsmObOnauQkBC98sorGjhwoL744osSLT1FTZw4URMnTtSoUaN05ZVXateuXbr55puVn5+vNm3auJXt06ePli9fLmOM1xg5PfTQQ+rcubP++9//KjMzU8nJyerTp4/Wrl2rU0891VXOn48hACjBAAB8Lj093Ugy1157rddy11xzjZFk/vrrL9vLlGXEiBFGknnttdfcpr/zzjtGkvnggw/cpq9atcpIMi+++KIxxph58+YZSWbdunUet7F06VIjySxdutRt+rZt24wkM3v2bNe0CRMmmOKnmzPPPNP07t27xHoHDx5szj77bBt76e7BBx80ksx3333nNv22224zDofDbN682a1+HTp0MHl5ea5y33//vZFk3nnnHa/bcS7/zDPPuE2fPXu2kWS2bdtmjDHmyJEjpmHDhmbIkCFu5fLz803Hjh3Nueee63HZjIwMEx4ebi6//HK3ZVesWGEklYjbRRddZIKCgrzW25gTv7POnTubgoIC1/Tt27ebkJAQc9NNN7mmVcUxBAC+RPczAKhGpvDuevHRwXzhb3/7m9vnTz/9VPXr19eQIUOUl5fn+jn77LMVFxfn6kp29tlnKzQ0VLfccovmzp2r33//3ed18+Tcc8/Vjz/+qDFjxuiLL75QVlaWreW++uortWvXTueee67b9JEjR7pawIq69NJLFRQU5Pp81llnSZJ27NhxkntgSUlJ0YEDBzRixAi3WBcUFOjiiy/WqlWrdOTIkVKXTU1N1fHjx3Xddde5Te/Ro4eSkpJKlP/yyy+Vl5dnu27Dhw93O96SkpLUo0cPLV26tETZQDyGANROJDUAUAkaN26syMhIbdu2zWu57du3KyIiQo0aNbK9jB2RkZGqV6+e27Q///xTBw8eVGhoqEJCQtx+0tPTtW/fPklSq1attGTJEjVt2lS33367WrVqpVatWtl6ruVkjR8/XlOnTtXKlSt1ySWXqFGjRurbt69Wr17tdbn9+/eXOvJYQkKCa35RjRo1cvvs7EZ27Nixk6m+y59//ilJuvLKK0vE+umnn5YxxuPQ3M66xsXFlZhX2rTy8rTe4jEK1GMIQO3EMzUAUAmCgoJ00UUX6fPPP9fu3btLfa5m9+7dWrNmjS6++GLXMn379vW6jF2ltfw0btxYjRo18jh6WnR0tOv/vXr1Uq9evZSfn6/Vq1e7hqaOjY3Vtddeq/DwcElSdna22zqcF7UVFRwcrHvvvVf33nuvDh48qCVLluihhx7SwIEDtWvXLo/PYjRq1EhpaWklpu/Zs0eSte9Vybm9GTNmqFu3bqWWiY2NLXW6M+FKT08vMS89Pd01OEFFeVpv8USvso8hAPAlWmoAoJI8+OCDMsZozJgxys/Pd5uXn5+v2267Tfn5+br77rtd08ePHy9jjG6++Wbl5OSUWGdubq7+97//Vag+gwcP1v79+5Wfn6+uXbuW+Cn+ALpkJVrnnXeeXnjhBUnSDz/8IEmuC+uffvrJrfwnn3xiqy5hYWFltorUr19fV155pW6//XYdOHDA68sp+/btq40bN7rq5/T666/L4XDowgsvtFUvX+nZs6fq16+vjRs3lhrrrl27KjQ0tNRlu3XrpvDwcL311ltu01NSUnzSPe6dd95xG1Rgx44dSklJsfVST18eQwDgS7TUAEAl6dmzp6ZPn667775b559/vu644w41b97c9fLN1NRUJScnq3///q5lunfvrpdeekljxoxRly5ddNttt+nMM89Ubm6u1q5dq1dffVXt27fXkCFDyl2fa6+9Vm+99ZYGDRqku+++W+eee65CQkK0e/duLV26VMOGDdPll1+ul19+WV999ZUuvfRSNW/eXMePH9drr70mSerXr58kq7tSv379NHnyZDVo0EBJSUn68ssv9eGHH9qqS4cOHfTuu+/qvffe06mnnqrw8HB16NBBQ4YMUfv27dW1a1c1adJEO3bs0PTp05WUlKTWrVt7XN8999yj119/XZdeeqkee+wxJSUl6bPPPtOLL76o2267Taeffnq543Uy6tatqxkzZmjEiBE6cOCArrzySjVt2lR//fWXfvzxR/3111966aWXSl22QYMGuv/++/XEE0/opptu0lVXXaVdu3YpOTm51K5jffv21fLly20/V7N3715dfvnluvnmm5WZmakJEyYoPDxc48ePL3NZXx5DAOBT1TlKAQDUBikpKeZvf/ubiY2NNXXq1DGSTHh4uPnss888LrNu3TozYsQI07x5cxMaGmqioqJMp06dzKOPPmr27t3rdXsjRowwUVFRpc7Lzc01U6dONR07djTh4eGmbt26pm3btmb06NHmt99+M8YYk5qaai6//HKTlJRkwsLCTKNGjUzv3r3NJ5984rautLQ0c+WVV5qGDRuamJgYc/3115vVq1fbGv1s+/btZsCAASY6OtpIMklJScYYY5599lnTo0cP07hxYxMaGmqaN29uRo0aZbZv3+51n40xZseOHWb48OGmUaNGJiQkxLRp08Y888wzJj8/31XG0+hlxhgjyUyYMMHrNuyOfua0fPlyc+mll5qGDRuakJAQ06xZM3PppZea999/3+uyBQUFZvLkySYxMdGEhoaas846y/zvf/8zvXv3LjH6We/evUvEtzTO0c/eeOMNc9ddd5kmTZqYsLAw06tXL7N69Wq3slV1DAGArziMsTmwPQDAJ15//XWNGDFC48aN09NPP13d1UEtsWzZMl144YV6//33deWVV1Z3dQDAp+h+BgBV7B//+IfS0tL04IMPKioqSo8++mh1VwkAgIBGUgMA1eCf//yn/vnPf1Z3NQAAqBHofgYAAAAgoDGkMwAAAICARlIDAAAAIKCR1AAAAAAIaAwUYFNBQYH27Nmj6OhoORyO6q4OAAAAUKMZY3To0CElJCSoTh3vbTEkNTbt2bNHiYmJ1V0NAAAAoFbZtWuXTjnlFK9lSGpsio6OlmQFtV69etVcG0tubq4WLVqkAQMGKCQk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5sEHHzTGWBfrpR0kdowbN85cf/31Zvbs2SW+4MeNG2fatm3rNm306NGmW7duFdpWZauMODm/3NeuXeuxTE2L0zXXXGOuv/76cq3zxRdfNDExMeb48eOuaZMnTzYJCQmmoKDAGBN4cTKmcmJVlKdjMtBiVdlxcho0aJC58cYbXZ+JU+nuuecec/7557s+E6cTatI5z5jKiVVtPO8VN2zYMHPRRRd5XWdNPO9VRpyK8qdzHt3PqkFOTo7WrFmjAQMGuE0fMGCAUlJSXJ+XLVumpk2b6vTTT9fNN9+svXv3upUfOXKk+vTp4zbtq6++0vvvv68XXnih1G2npqaW2O7AgQO1evXqEl0bqltlxkmShg4dqqZNm6pnz56aN2+e27yaFKeCggJ99tlnOv300zVw4EA1bdpU5513nj766CO38sXjlJqaqt69e7u9bGvgwIHas2ePtm/f7ioTKHGSKi9WdgRSrKoyTpmZmWrYsKHrM3EqacuWLVq4cKF69+7tmkacLDXpnCdV/jFVW857xf3555/67LPPNGrUKLfpNf28V1lxsqM64kRSUw327dun/Px8xcbGuk2PjY1Venq6JOmSSy7RW2+9pa+++krPPvusVq1apYsuukjZ2dmu8vHx8WrevLnr8/79+zVy5EjNmTNH9erVK3Xb6enppW43Ly9P+/bt89Uu+kRlxalu3bqaNm2a5s2bpwULFqhv37665ppr9Oabb7rK1KQ47d27V4cPH9ZTTz2liy++WIsWLdLll1+uK664QsuXL3eVLx4nTzFwzvNWxh/jJFVerOwIpFhVVZzmzZunVatW6cYbb3RNI04n9OjRQ+Hh4WrdurV69eqlxx57zDWPONW8c55UebGqbee94ubOnavo6GhdccUVbtNr+nmvsuJkR3XEKbhS1gpbHA6H22djjGvaNddc45revn17de3aVUlJSfrss89cB9vkyZPdlr/55ps1fPhwXXDBBeXebmnT/YWv49S4cWPdc889rs9du3ZVRkaGpkyZouuvv97rdkub7i88xamgoECSNGzYMNd+n3322UpJSdHLL7/suvtbPE6e1ll8eqDFSaqcWFV0u6VN9xeVGadly5Zp5MiRmjlzps4888wyt1vadH9RWXF67733dOjQIf3444964IEHNHXqVI0bN87rdkub7i98Haeaes6TfB+r2nbeK+61117Tddddp/DwcLfpteW8Vxlxquh2S5vuK7TUVIPGjRsrKCioRJa8d+/eElmtU3x8vJKSkvTbb795XO9XX32lqVOnKjg4WMHBwRo1apQyMzMVHBys1157TZIUFxdX6naDg4PVqFGjk9wz36qsOJWmW7dubsvUpDg1btxYwcHBateundv8M844w+toOZ5iIJ24cxVIcZIqL1Z2BFKsKjtOy5cv15AhQzRt2jT94x//cJtHnE5ITExUu3bt9Pe//11PPfWUkpOTlZ+fL4k4STXvnCdV7XdUTT7vFfXNN99o8+bNuummm8pcb00771VWnOyojjiR1FSD0NBQdenSRYsXL3abvnjxYvXo0aPUZfbv369du3YpPj7e43pTU1O1bt06189jjz2m6OhorVu3TpdffrkkqXv37iW2u2jRInXt2lUhISEnuWe+VVlxKs3atWvdlqlJcQoNDdU555yjzZs3u83/9ddflZSU5HG93bt319dff62cnBzXtEWLFikhIUEtWrRwlQmUOEmVFys7AilWlRmnZcuW6dJLL9VTTz2lW265pcR84lQ6Y4xyc3NddzqJU80750lVe0zV5PNeUbNmzVKXLl3UsWPHMtdb0857lRUnO6olTpU2BAG8cg6xN2vWLLNx40YzduxYExUVZbZv324OHTpk7rvvPpOSkmK2bdtmli5darp3726aNWtmsrKyXOt48MEHzQ033OBxG6WNBOMcYu+ee+4xGzduNLNmzQqIoQh9Gac5c+aYt956y2zcuNFs2rTJPPPMMyYkJMRMmzbNVaYmxckYYz788EMTEhJiXn31VfPbb7+ZGTNmmKCgIPPNN9+41lE8TgcPHjSxsbHm73//u1m/fr358MMPTb169Uod2jJQ4mRM5cTKGGN+/vlns3btWjNkyBDTp08fs3btWreRhgItVpURp6VLl5rIyEgzfvx4j8OwEydj3nzzTfPee++ZjRs3mq1bt5r/+7//M82aNTPXXXedqwxxKqkmnPOMqZxY1cbznjHWEM2RkZHmpZdeKnUdteG8VxlxMsY/z3kkNdXohRdeMElJSSY0NNR07tzZLF++3BhjzNGjR82AAQNMkyZNTEhIiGnevLkZMWKE2blzp9vyI0aMML179/a4/tK+4I0xZtmyZaZTp04mNDTUtGjRwuNB7C98Hac5c+aYM844w0RGRpro6GjTpUsX88Ybb5TYbk2Jk9OsWbPMaaedZsLDw03Hjh3NRx995Da/tOPpp59+Mr169TJhYWEmLi7OJCcnu4a1dAq0OBlTObFKSkoykkr8FBVosfJ1nEaMGFFqjIrHsrbH6d133zWdO3c2devWNVFRUaZdu3Zm0qRJ5tixY27L1fY4FVdTznnG+D5WtfW898orr5iIiAhz8ODBUpevLee9yoiTP57zHMYUe00qAAAAAAQQnqkBAAAAENBIagAAAAAENJIaAAAAAAGNpAYAAABAQCOpAQAAABDQSGoAAAAABDSSGgAAAAABjaQGAAAAQEAjqQEQEObMmaP69etXdzUAAIAfIqkBIEnq06ePxo4dW2L6Rx99JIfD4SrjcDg8/rRo0UKSlJ6erjvvvFOnnnqqwsLClJiYqCFDhujLL78sddstWrTwut4+ffrommuu0a+//lpZu19hDodDH330kU/WtX37djkcDq1bt84n6/MkOTlZZ599dpnlZs6cqV69eqlBgwZq0KCB+vXrp++//75EuRdffFEtW7ZUeHi4unTpom+++cZtvjFGycnJSkhIUEREhPr06aOff/7ZrUx6erpuuOEGxcXFKSoqSp07d9a8efPKrOPOnTs1ZMgQRUVFqXHjxrrrrruUk5NTYvtTp07V6aef7joeJ02aVOa6P/jgA7Vr105hYWFq166d5s+f7zb/66+/1pAhQ5SQkFDiOHD+Lr39JCcn29oHT+tauHCh1/onJyerbdu2ioqKcv3+vvvuO7cypf1NX3vttWXGZvny5erSpYvCw8N16qmn6uWXXy53/Eqzfv169e7dWxEREWrWrJkee+wxGWMqZdu+OG6zs7N15513qnHjxoqKitLQoUO1e/dutzIZGRm64YYbFBMTo5iYGN1www06ePCgWxk7x7Gd2AC1mgEAY0zv3r3N3XffXWL6/PnzjfOrYv/+/SYtLc2kpaWZ77//3kgyS5YscU3bu3ev2bZtm0lISDDt2rUz77//vtm8ebPZsGGDefbZZ02bNm1K3fbevXtd6/jggw+MJLN582bXtP3791fmrp8USWb+/PknvZ7s7Gyzbds2I8msXbv2pNfnzYQJE0zHjh3LLDd8+HDzwgsvmLVr15pffvnF3HjjjSYmJsbs3r3bVebdd981ISEhZubMmWbjxo3m7rvvNlFRUWbHjh2uMk899ZSJjo42H3zwgVm/fr255pprTHx8vMnKynKV6devnznnnHPMd999Z7Zu3Woef/xxU6dOHfPDDz94rF9eXp5p3769ufDCC80PP/xgFi9ebBISEswdd9zhVu7OO+80bdq0MR9//LH5/fffzdq1a83ixYu97ntKSooJCgoykyZNMr/88ouZNGmSCQ4ONitXrnSVWbBggXn44Yddx2zR4yAvL891/KalpZn77rvPnHnmmW7TDh06ZGsfnMdF0b+1tLQ0k52d7XUf3nrrLbN48WKzdetWs2HDBjNq1ChTr149s3fvXleZ3r17m5tvvtltvQcPHvS63t9//91ERkaau+++22zcuNHMnDnThISEmHnz5pUrfsVlZmaa2NhYc+2115r169ebDz74wERHR5upU6f6fNu+Om5vvfVW06xZM7N48WLzww8/mAsvvNB07NjR5OXlucpcfPHFpn379iYlJcWkpKSY9u3bm8GDB7vm2zkG7MQGqO1IagAYY+wlNUV5ugC/5JJLTLNmzczhw4dLLJORkVFmPZYuXWoklSg7e/ZsExMT4/rsvDCfNWuWSUxMNFFRUebWW281eXl55umnnzaxsbGmSZMm5oknnnBbz8GDB83NN99smjRpYqKjo82FF15o1q1b57E+2dnZ5vbbbzdxcXEmLCzMJCUlmUmTJhljjElKSjKSXD9JSUnGGGO2bNlihg4dapo2bWqioqJM165dS1xEJyUlmccff9yMGDHC1KtXz/zjH/9wW5ck07t3b1dMzjnnHBMZGWliYmJMjx49zPbt2z3Wedy4caZ169YmIiLCtGzZ0jzyyCMmJyfHFcfi25k9e7aX38gJeXl5Jjo62sydO9c17dxzzzW33nqrW7m2bduaBx980BhjTEFBgYmLizNPPfWUa/7x48dNTEyMefnll13ToqKizOuvv+62noYNG5r//ve/HuuzYMECU6dOHfPHH3+4pr3zzjsmLCzMZGZmGmOM2bhxowkODjabNm2ytY9OV199tbn44ovdpg0cONBce+21pZYvK7n1lEja2QdfJbuZmZmu5MjJ09+9N+PGjTNt27Z1mzZ69GjTrVs31+fyxs8YY1588UUTExNjjh8/7po2efJkk5CQYAoKCny6bV8ctwcPHjQhISHm3XffdZX5448/TJ06dczChQuNMdbxJ8ktoUpNTTWSXMeknWPATmyA2o7uZwB85sCBA1q4cKFuv/12RUVFlZjv62ditm7dqs8//1wLFy7UO++8o9dee02XXnqpdu/ereXLl+vpp5/WI488opUrV0qyupNceumlSk9P14IFC7RmzRp17txZffv21YEDB0rdxn/+8x998skn+r//+z9t3rxZb775pqub3apVqyRJs2fPVlpamuvz4cOHNWjQIC1ZskRr167VwIEDNWTIEO3cudNt3c8884zat2+vNWvW6F//+pera9eSJUuUlpamDz/8UHl5ebrsssvUu3dv/fTTT0pNTdUtt9zi6hJYmujoaM2ZM0cbN27Uv//9b82cOVPPPfecJOmaa67RfffdpzPPPFNpaWlKS0vTNddcYyveR48eVW5urho2bChJysnJ0Zo1azRgwAC3cgMGDFBKSookadu2bUpPT3crExYWpt69e7vKSNL555+v9957TwcOHFBBQYHeffddZWdnq0+fPh7rk5qaqvbt2yshIcE1beDAgcrOztaaNWskSf/73/906qmn6tNPP1XLli3VokUL3XTTTR5/30XXXXy/Bg4c6FZnX7CzD05Dhw5V06ZN1bNnzxJd85xd1JYtW1bqdnJycvTqq68qJiZGHTt2dJv31ltvqXHjxjrzzDN1//3369ChQ27z+/Tpo5EjR7rVubTYrF69Wrm5uV7LeItfamqqevfurbCwMLdl9uzZo+3bt/ts2746btesWaPc3Fy3MgkJCWrfvr2rTGpqqmJiYnTeeee5ynTr1k0xMTFuZco6BuzEBqjtgqu7AgBqji1btsgYo7Zt21bJ9goKCvTaa68pOjpa7dq104UXXqjNmzdrwYIFqlOnjtq0aaOnn35ay5YtU7du3bR06VKtX79ee/fudV0cTJ06VR999JHmzZunW265pcQ2du7cqdatW+v888+Xw+FQUlKSa16TJk0kWclaXFyca3rHjh3dLhyfeOIJzZ8/X5988onuuOMO1/SLLrpI999/v+uz8+KkUaNGrvUdOHBAmZmZGjx4sFq1aiVJOuOMM7zG5ZFHHnH9v0WLFrrvvvv03nvvady4cYqIiFDdunUVHBzsVmc7HnzwQTVr1kz9+vWTJO3bt0/5+fmKjY11KxcbG6v09HRJcv1bWpkdO3a4Pr/33nu65ppr1KhRIwUHBysyMlLz58937XNp0tPTS6y3QYMGCg0NdW33999/144dO/T+++/r9ddfV35+vu655x5deeWV+uqrr8q17qL75St29qFu3bqaNm2aevbsqTp16uiTTz7RNddco7lz5+r666+XJIWEhKhNmzaKjIx0W9enn36qa6+9VkePHlV8fLwWL16sxo0bu+Zfd911atmypeLi4rRhwwaNHz9eP/74oxYvXuwq07x5c8XHx3utc2xsrPLy8rRv3z7Fx8dXKH7p6emuGwZFl3HOa9mypU+27avjNj09XaGhoWrQoIHX9TRt2rTEvjZt2tStTFnHgJ3YALUdSQ0AnzGFD616a0XwpRYtWig6Otr1OTY2VkFBQapTp47btL1790qy7qwePnxYjRo1clvPsWPHtHXr1lK3MXLkSPXv319t2rTRxRdfrMGDB5e4w1vckSNHNHHiRH366afas2eP8vLydOzYsRItNV27di1zHxs2bKiRI0dq4MCB6t+/v/r166err77a7SKzuHnz5mn69OnasmWLDh8+rLy8PNWrV6/MbXkzZcoUvfPOO1q2bJnCw8Pd5hX/fRtjSkwrq8wjjzyijIwMLVmyRI0bN9ZHH32kq666St988406dOigSy65xPUgd1JSkuuB7dKOtaLrLigoUHZ2tl5//XWdfvrpkqRZs2apS5cu2rx5syIiItSuXTvXsg899JAeeugh2/vlC2XtQ+PGjXXPPfe45nXt2lUZGRmaMmWKK6lp1qyZNm3aVGI9F154odatW6d9+/Zp5syZuvrqq/Xdd9+5LrRvvvlmV9n27durdevW6tq1q3744Qd17txZkvT666+XWefS/vYrEr+Krrci2/ZVmeKKlynr91vRMlX9fQv4O7qfAZAk1atXT5mZmSWmHzx40PYFcevWreVwOPTLL7/4unqlCgkJcfvscDhKnVZQUCDJusCNj4/XunXr3H42b96sBx54oNRtdO7cWdu2bdPjjz+uY8eO6eqrr9aVV17ptV4PPPCAPvjgAz355JP65ptvtG7dOnXo0KHEaEalddErzezZs5WamqoePXrovffe0+mnn+7qUlfcypUrde211+qSSy7Rp59+qrVr1+rhhx8use3ymDp1qiZNmqRFixbprLPOck1v3LixgoKCStx937t3r+susrM1yFuZrVu36vnnn9drr72mvn37qmPHjpowYYK6du2qF154QZL03//+1/X7WrBggWvdxdebkZGh3Nxc17rj4+MVHBzsSmikEy1dO3fuVEJCgtuxcOutt3pcd9E6+4qdfShNt27d9Ntvv5W5/qioKJ122mnq1q2bZs2apeDgYM2aNctj+c6dOyskJMTruj3FJjg42HXDoCLx87SMJLfj6WS37avjNi4uTjk5OcrIyPBa5s8//yyxr3/99ZfXfSp+DNiJDVDbkdQAkCS1bdtWq1evLjF91apVatOmja11NGzYUAMHDtQLL7ygI0eOlJhffBjTqta5c2elp6crODhYp512mttP0S45xdWrV0/XXHONZs6cqffee08ffPCB65mMkJAQ5efnu5X/5ptvNHLkSF1++eXq0KGD4uLibPV7Dw0NlaQS65OkTp06afz48UpJSVH79u319ttvl7qOFStWKCkpSQ8//LC6du2q1q1bu3Xzcm6ntG2U5plnntHjjz+uhQsXlmhZCg0NVZcuXdy6KknS4sWL1aNHD0lydW0qWiYnJ0fLly93lTl69KgkubWwSVJQUJArIW3WrJnrd+XsAti9e3dt2LBBaWlprmUWLVqksLAwdenSRZLUs2dP5eXlubXEOYcGT0pKKnEsOJ8X6t69e4n9WrRokavOvmJnH0qzdu1ar611nhhjlJ2d7XH+zz//rNzcXK/r9hSbrl27um4qVCR+3bt319dff+2WgC9atEgJCQmurle+2LavjtsuXbooJCTErUxaWpo2bNjgKtO9e3dlZma6DYX+3XffKTMz061MWceAndgAtV7VjksAwF9t27bNREREmDFjxph169aZzZs3m+eff96EhYWZ//u//yu1vEoZken33383cXFxpl27dmbevHnm119/NRs3bjT//ve/S4xaVJryjn5W1IgRI8ywYcPcphUd3amgoMCcf/75pmPHjmbhwoVm27ZtZsWKFebhhx82q1atKrU+06ZNM++884755ZdfzObNm82oUaNMXFycyc/PN8YY07p1a3PbbbeZtLQ0c+DAAWOMMZdddpk5++yzzdq1a826devMkCFDTHR0tNsoU0lJSea5555z21Zubq6JiIgwTzzxhElPTzcHDx40v//+u3nwwQdNSkqK2b59u/niiy9Mw4YNzYsvvlhqfT/66CMTHBxs3nnnHbNlyxbz73//2zRs2NAtdm+99ZaJiooya9euNX/99ZfbiEpFPf300yY0NNTMmzevxFDETs6hcWfNmmU2btxoxo4da6KiotxGZ3vqqadMTEyM+fDDD8369evN3//+d7ehcXNycsxpp51mevXqZb777juzZcsWM3XqVONwOMxnn31Wat2MOTEUbt++fc0PP/xglixZYk455RS3oXDz8/NN586dzQUXXGB++OEHs3r1anPeeeeZ/v37e1yvMcasWLHCBAUFmaeeesr88ssv5qmnnioxLPChQ4fM2rVrzdq1a40kM23aNLN27Vq3YYGdPI1+Zmcf5syZY9566y2zceNGs2nTJvPMM8+YkJAQM23aNFeZ3bt3mzZt2pjvvvvOGGPM4cOHzfjx401qaqrZvn27WbNmjRk1apQJCwszGzZsMMZYo/RNnDjRrFq1ymzbts189tlnpm3btqZTp05uQxLfcMMNrlHBjDkxrPI999xjNm7caGbNmlViWGU78ZsxY4a56KKLXJ8PHjxoYmNjzd///nezfv168+GHH5p69eqVOqTzyW7bF8etMdaQzqeccopZsmSJ+eGHH8xFF11U6pDOZ511lklNTTWpqammQ4cOpQ7p7O0YsBMboLYjqQHgsnr1ajNw4EDTtGlTU69ePdO1a1fzzjvvlFrW2zCze/bsMbfffrtJSkoyoaGhplmzZmbo0KFm6dKlZdahMpMaY4zJysoyd955p0lISDAhISEmMTHRXHfddWbnzp2l1ufVV181Z599tomKijL16tVzXXg4ffLJJ+a0004zwcHBriGdt23bZi688EITERFhEhMTzfPPP1+iHqUlNcYYM3PmTJOYmGjq1KljevfubdLT081ll11m4uPjTWhoqElKSjKPPvqoK6kqzQMPPGAaNWpk6tata6655hrz3HPPucXu+PHj5m9/+5upX7++1yGdiw9Z7fyZMGGCW7kXXnjB9bvu3LmzWb58udv8goICM2HCBNew2BdccIFZv369W5lff/3VXHHFFaZp06YmMjLSnHXWWSWGeC7Njh07zKWXXmoiIiJMw4YNzR133FEiSfvjjz/MFVdcYerWrWtiY2PNyJEjbb376P333zdt2rQxISEhpm3btuaDDz5wm+88Vov/jBgxosS6vL0bqKx9mDNnjjnjjDNMZGSkiY6ONl26dDFvvPGG2zqcf4/Ov7Fjx46Zyy+/3CQkJJjQ0FATHx9vhg4dar7//nvXMjt37jQXXHCBadiwoQkNDTWtWrUyd911V4nY9O7du8Q+LVu2zHTq1MmEhoaaFi1a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e5E+b1xcnObPn1/nNlviea+p63TGnvMcmsXSpUtdQkKCW7Rokdu5c6e7//77Xdu2bd3evXudc85NnDjRjR492hUVFYVfhw4dimrbx48fd4MHD3bXX3+9S0tLqzLv008/dW3atHH333+/27lzp1u0aJFLSEhwy5cvb+qP2CRiUafPPvvMSXJr1qypsl5ZWVl4mZZWp127drmMjAz3wAMPuPfff9/t3r3bvfrqq+7zzz+vdZvFxcWuS5cu7kc/+pHbsWOHe+GFF1xqaqp77LHHwsv4VifnYlOrr7/+2t19993umWeecXl5eW7s2LE1lvGtVrGo09y5c91DDz3k3n77bbdr1y73xBNPuFatWrmXX345vAx1cm737t1uyZIlbtu2bW7Pnj3upZdecp07d3YzZswIL0OdKrSkc55zsanV2Xjeq/w5i4qK3JIlS1xcXJzbvXt3rdtsiee9WNTpTD3nEWqayWWXXebuvvvuKtN69+7tpk+f7pyzznqkgyQa06ZNcxMmTHC///3va/wPftq0aa53795Vpt11111u6NChDdpXrMWiTqH/uW/durXWZVpanW699VY3YcKEem1z4cKFLi0tzX377bfhafPmzXPdunVz5eXlzjn/6uRcbGpVWW3HpG+1inWdQm644QZ3xx13hN9Tp8imTp3qvv/974ffU6cKLemc51xsanU2nveqGzt2rBs5cmSd22yJ571Y1KmyM+mcx+VnzeD48ePasmWLRo0aVWX6qFGjtGnTpvD7devWqXPnzrrwwguVn5+vL774osrykyZN0ogRI6pMe+utt/T888/rqaeeirjvzZs319hvXl6e3nvvvRqXNjS3WNZJksaMGaPOnTvryiuv1PLly6vMa0l1Ki8v12uvvaYLL7xQeXl56ty5s4YMGaIXX3yxyvLV67R582YNHz68yh/bysvL04EDB7Rnz57wMr7USYpdraLhU61OZ52Ki4uVkZERfk+datq1a5def/11DR8+PDyNOpmWdM6TYn9MnS3nveo+//xzvfbaa7rzzjurTG/p571Y1SkazVEnQk0z+Oqrr3Ty5El16dKlyvQuXbro4MGDkqTrr79ezz33nN566y39+te/1rvvvquRI0eqrKwsvHzXrl2VlZUVfn/o0CFNmjRJBQUFat++fcR9Hzx4MOJ+T5w4oa+++qqpPmKTiFWd2rVrpwULFmj58uX661//qmuuuUa33nqrnn322fAyLalOX3zxhb7++ms9+uijGj16tFatWqVbbrlF48aN0/r168PLV69TbTUIzatrmTOxTlLsahUNn2p1uuq0fPlyvfvuu7rjjjvC06hThSuuuELJycm64IILNGzYMM2ZMyc8jzq1vHOeFLtanW3nver+8Ic/KDU1VePGjasyvaWf92JVp2g0R53iY7JVRCUuLq7Ke+dceNqtt94ant6vXz8NHjxY2dnZeu2118IH27x586qsn5+frx//+Me66qqr6r3fSNPPFE1dp44dO2rq1Knh94MHD9bhw4f1q1/9ShMmTKhzv5Gmnylqq1N5ebkkaezYseHPPWDAAG3atElPP/10+Nvf6nWqbZvVp/tWJyk2tWrofiNNP1PEsk7r1q3TpEmTtGjRIvXt2/eU+400/UwRqzotW7ZMpaWl+uCDD/TAAw/oscce07Rp0+rcb6TpZ4qmrlNLPedJTV+rs+28V92SJUt0++23Kzk5ucr0s+W8F4s6NXS/kaY3FUZqmkHHjh3VunXrGin5iy++qJFqQ7p27ars7Gx98skntW73rbfe0mOPPab4+HjFx8frzjvvVHFxseLj47VkyRJJUmZmZsT9xsfHq0OHDo38ZE0rVnWKZOjQoVXWaUl16tixo+Lj49WnT58q8y+66KI6n5ZTWw2kim+ufKqTFLtaRcOnWsW6TuvXr9fNN9+sBQsW6Kc//WmVedSpQo8ePdSnTx/ddtttevTRRzVr1iydPHlSEnWSWt45Tzq9/49qyee9yjZu3KjCwkL97Gc/O+V2W9p5L1Z1ikZz1IlQ0wwSExOVm5ur1atXV5m+evVqXXHFFRHXOXTokPbv36+uXbvWut3Nmzdr27Zt4decOXOUmpqqbdu26ZZbbpEkXX755TX2u2rVKg0ePFgJCQmN/GRNK1Z1imTr1q1V1mlJdUpMTNSll16qwsLCKvP/9a9/KTs7u9btXn755dqwYYOOHz8enrZq1Sp169ZN5513XngZX+okxa5W0fCpVrGs07p163TjjTfq0Ucf1eTJk2vMp06ROef03Xffhb/ppE4t75wnnd5jqiWf9ypbvHixcnNz1b9//1Nut6Wd92JVp2g0S51i9ggC1Cn0iL3Fixe7nTt3uilTpri2bdu6PXv2uNLSUvfzn//cbdq0yX322Wdu7dq17vLLL3fdu3d3JSUl4W1Mnz7d/eQnP6l1H5GeBBN6xN7UqVPdzp073eLFi714FGFT1qmgoMA999xzbufOne6f//ynmz9/vktISHALFiwIL9OS6uSccytWrHAJCQnumWeecZ988on7zW9+41q3bu02btwY3kb1Oh05csR16dLF3XbbbW7Hjh1uxYoVrn379hEfbelLnZyLTa2cc+6jjz5yW7dudTfffLMbMWKE27p1a5UnDflWq1jUae3ata5NmzZuxowZtT6GnTo59+yzz7ply5a5nTt3ut27d7u//OUvrnv37u72228PL0OdamoJ5zznYlOrs/G855w9orlNmzbut7/9bcRtnA3nvVjUybkz85xHqGlGTz31lMvOznaJiYlu0KBBbv369c45544ePepGjRrlOnXq5BISElxWVpabOHGi27dvX5X1J06c6IYPH17r9iP9D94559atW+cGDhzoEhMT3XnnnVfrQXymaOo6FRQUuIsuusi1adPGpaamutzcXPenP/2pxn5bSp1CFi9e7Hr16uWSk5Nd//793YsvvlhlfqTjafv27W7YsGEuKSnJZWZmulmzZoUfaxniW52ci02tsrOznaQar8p8q1VT12nixIkRa1S9lmd7nZYuXeoGDRrk2rVr59q2bev69OnjfvnLX7pjx45VWe9sr1N1LeWc51zT1+psPe/97ne/cykpKe7IkSMR1z9bznuxqNOZeM6Lc67an0kFAAAAAI9wTw0AAAAArxFqAAAAAHiNUAMAAADAa4QaAAAAAF4j1AAAAADwGqEGAAAAgNcINQAAAAC8RqgBAAAA4DVCDQAAAACvEWoAAAAAeI1QAwAAAMBrhBoAAAAAXvv/ARzsCjc6tLQAAAAASUVORK5CYII=", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# QC Plot\n", - "try:\n", - " qc_variable = df_filter_3.var_name.values[0]\n", - " print(qc_variable)\n", - "except Exception as e:\n", - " print(e)\n", - " \n", - "try:\n", - "\n", - " # Plot\n", - " qc_display = act.plotting.TimeSeriesDisplay(ds_act)\n", - " qc_display.add_subplots((2,), figsize = (9.5,10))\n", - " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", - " qc_ax.grid()\n", - " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", - " qc_ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", - "\n", - " plt.show()\n", - "except Exception as e:\n", - " print(e)" - ] - }, - { - "cell_type": "markdown", - "id": "d76e4d27", - "metadata": {}, - "source": [ - "#### bonus: choose variables to plot from a dropdown menu " - ] - }, - { - "cell_type": "code", - "execution_count": 32, - "id": "bb733804", - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
var_namedimsis_dimn_dimattrsdtype
4pres(time,)False1{'long_name': 'Barometric pressure', 'units': ...float32
6tdry(time,)False1{'long_name': 'Dry bulb temperature', 'units':...float32
8dp(time,)False1{'long_name': 'Dewpoint temperature', 'units':...float32
10wspd(time,)False1{'long_name': 'Wind speed', 'units': 'm/s', 'v...float32
12deg(time,)False1{'long_name': 'Wind direction', 'units': 'deg'...float32
14rh(time,)False1{'long_name': 'Relative humidity', 'units': '%...float32
16u_wind(time,)False1{'long_name': 'Eastward wind component', 'unit...float32
18v_wind(time,)False1{'long_name': 'Northward wind component', 'uni...float32
20wstat(time,)False1{'long_name': 'Wind status', 'units': 'unitless'}float32
21asc(time,)False1{'long_name': 'Ascent rate', 'units': 'm/s', '...float32
23rh_smooth(time,)False1{'long_name': 'Smoothed original relative humi...float32
25rh_biased(time,)False1{'long_name': 'Dry bias corrected relative hum...float32
27rh_adjust(time,)False1{'long_name': 'Final corrected ambient relativ...float32
29rh_scaled(time,)False1{'long_name': 'Scaled final corrected ambient ...float32
31dp_scaled(time,)False1{'long_name': 'Scaled dewpoint temperature', '...float32
\n", - "
" - ], - "text/plain": [ - " var_name dims is_dim n_dim \\\n", - "4 pres (time,) False 1 \n", - "6 tdry (time,) False 1 \n", - "8 dp (time,) False 1 \n", - "10 wspd (time,) False 1 \n", - "12 deg (time,) False 1 \n", - "14 rh (time,) False 1 \n", - "16 u_wind (time,) False 1 \n", - "18 v_wind (time,) False 1 \n", - "20 wstat (time,) False 1 \n", - "21 asc (time,) False 1 \n", - "23 rh_smooth (time,) False 1 \n", - "25 rh_biased (time,) False 1 \n", - "27 rh_adjust (time,) False 1 \n", - "29 rh_scaled (time,) False 1 \n", - "31 dp_scaled (time,) False 1 \n", - "\n", - " attrs dtype \n", - "4 {'long_name': 'Barometric pressure', 'units': ... float32 \n", - "6 {'long_name': 'Dry bulb temperature', 'units':... float32 \n", - "8 {'long_name': 'Dewpoint temperature', 'units':... float32 \n", - "10 {'long_name': 'Wind speed', 'units': 'm/s', 'v... float32 \n", - "12 {'long_name': 'Wind direction', 'units': 'deg'... float32 \n", - "14 {'long_name': 'Relative humidity', 'units': '%... float32 \n", - "16 {'long_name': 'Eastward wind component', 'unit... float32 \n", - "18 {'long_name': 'Northward wind component', 'uni... float32 \n", - "20 {'long_name': 'Wind status', 'units': 'unitless'} float32 \n", - "21 {'long_name': 'Ascent rate', 'units': 'm/s', '... float32 \n", - "23 {'long_name': 'Smoothed original relative humi... float32 \n", - "25 {'long_name': 'Dry bias corrected relative hum... float32 \n", - "27 {'long_name': 'Final corrected ambient relativ... float32 \n", - "29 {'long_name': 'Scaled final corrected ambient ... float32 \n", - "31 {'long_name': 'Scaled dewpoint temperature', '... float32 " - ] - }, - "execution_count": 32, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Valid variables filtering\n", - "exclude_substrings = [\"time\", \"lat\", \"lon\", \"alt\", \"qc\"]\n", - "df_filter_4 = df_info[(df_info.is_dim==False) &\n", - " (df_info.dims.apply(lambda x: \"time\" in x)) &\n", - " (~df_info.var_name.str.contains('|'.join(exclude_substrings)))\n", - " ]\n", - "df_filter_4" - ] - }, - { - "cell_type": "code", - "execution_count": 33, - "id": "82d5c20d", - "metadata": {}, - "outputs": [], - "source": [ - "# example 1: using xarray plot\n", - "\n", - "# Uncomment the following cell to try the interactive plot (ctrl + /)" - ] - }, - { - "cell_type": "code", - "execution_count": 34, - "id": "2fc9aaa4", - "metadata": {}, - "outputs": [], - "source": [ - "# %matplotlib widget\n", - "# plt.clf()\n", - "\n", - "# fig, ax = plt.subplots(figsize=(10, 4))\n", - "\n", - "# available_variables = df_filter_4.var_name.values\n", - "# @widgets.interact(var=available_variables)\n", - "# def update(var = available_variables[0]):\n", - "# fig.clear() # Remove old lines from plot and plot new one\n", - "# if len(ds[var].dims)==2:\n", - "# ds[var].plot(x=\"time\", add_colorbar=False)\n", - "# else:\n", - "# ds[var].plot()\n", - "# plt.grid()\n", - "# plt.show()" - ] - }, - { - "cell_type": "code", - "execution_count": 35, - "id": "fabdd802", - "metadata": {}, - "outputs": [], - "source": [ - "# example 2: using act plot\n", - "\n", - "# Uncomment the following cell to try the interactive plot (ctrl + /)" - ] - }, - { - "cell_type": "code", - "execution_count": 36, - "id": "962b4186", - "metadata": {}, - "outputs": [], - "source": [ - "# %matplotlib widget\n", - "# plt.clf()\n", - "\n", - "# available_variables = df_filter_4.var_name.values\n", - "\n", - "\n", - "# @widgets.interact(var=available_variables)\n", - "# def update(var = available_variables[0]):\n", - "\n", - "# i_display = act.plotting.TimeSeriesDisplay(ds_act)\n", - "# i_display.add_subplots((1,), figsize=(10, 4))\n", - "# ax = i_display.plot(var, subplot_index=(0,), set_title=f\"{var} ({ds_act[var].attrs['long_name']})\",)\n", - "\n", - "# ax.set_xlabel(f\"UTC Time starts at {ds.time.data[0]}\")\n", - "# ax.grid()\n", - "# plt.show()\n" - ] - }, - { - "cell_type": "markdown", - "id": "ba714aa6", - "metadata": {}, - "source": [ - "## Skew-T Plot" - ] - }, - { - "cell_type": "code", - "execution_count": 37, - "id": "606dc551", - "metadata": {}, - "outputs": [ - { - "ename": "NameError", - "evalue": "name 'files_list' is not defined", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)", - "Cell \u001b[0;32mIn[37], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m launch_times \u001b[38;5;241m=\u001b[39m [\u001b[38;5;28mstr\u001b[39m(datetime\u001b[38;5;241m.\u001b[39mstrptime(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;241m.\u001b[39mjoin(f\u001b[38;5;241m.\u001b[39msplit(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m.\u001b[39m\u001b[38;5;124m'\u001b[39m)[\u001b[38;5;241m-\u001b[39m\u001b[38;5;241m3\u001b[39m:\u001b[38;5;241m-\u001b[39m\u001b[38;5;241m1\u001b[39m]), \u001b[38;5;124m'\u001b[39m\u001b[38;5;124m%\u001b[39m\u001b[38;5;124mY\u001b[39m\u001b[38;5;124m%\u001b[39m\u001b[38;5;124mm\u001b[39m\u001b[38;5;132;01m%d\u001b[39;00m\u001b[38;5;124m%\u001b[39m\u001b[38;5;124mH\u001b[39m\u001b[38;5;124m%\u001b[39m\u001b[38;5;124mM\u001b[39m\u001b[38;5;124m%\u001b[39m\u001b[38;5;124mS\u001b[39m\u001b[38;5;124m'\u001b[39m)) \u001b[38;5;28;01mfor\u001b[39;00m f \u001b[38;5;129;01min\u001b[39;00m \u001b[43mfiles_list\u001b[49m]\n\u001b[1;32m 2\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mAvailable sonde launch times:\u001b[39m\u001b[38;5;124m'\u001b[39m)\n\u001b[1;32m 3\u001b[0m display(pd\u001b[38;5;241m.\u001b[39mDataFrame(launch_times, columns\u001b[38;5;241m=\u001b[39m[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mLaunch Time\u001b[39m\u001b[38;5;124m'\u001b[39m]))\n", - "\u001b[0;31mNameError\u001b[0m: name 'files_list' is not defined" - ] - } - ], - "source": [ - "launch_times = [str(datetime.strptime(''.join(f.split('.')[-3:-1]), '%Y%m%d%H%M%S')) for f in files_list]\n", - "print('Available sonde launch times:')\n", - "display(pd.DataFrame(launch_times, columns=['Launch Time']))\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d85d73a9", - "metadata": {}, - "outputs": [], - "source": [ - "# select sonde launch time from the list\n", - "launch_time_index = 0\n", - "sonde_file = files_list[launch_time_index]\n", - "sonde_ds = act.io.armfiles.read_netcdf(sonde_file)\n", - "\n", - "# Calculate stability indicies\n", - "sonde_ds = act.retrievals.calculate_stability_indicies(\n", - " sonde_ds, temp_name='tdry', td_name='dp', p_name='pres', rh_name='rh'\n", - ")\n", - "\n", - "# Set up plot\n", - "skewt = act.plotting.SkewTDisplay(sonde_ds, figsize=(7, 10))\n", - "\n", - "# Add data\n", - "skewt.plot_from_u_and_v('u_wind', 'v_wind', 'pres', 'tdry', 'dp', set_title=f'Skew-T Plot for {launch_times[launch_time_index]}')\n", - "sonde_ds.close()\n", - "plt.show()" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.16" - }, - "widgets": { - "application/vnd.jupyter.widget-state+json": { - "state": {}, - "version_major": 2, - "version_minor": 0 - } - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/VAPs/quicklook/SWFLUXANAL/.ipynb_checkpoints/1swfanalsirs1long.c1-checkpoint.ipynb b/VAPs/quicklook/SWFLUXANAL/.ipynb_checkpoints/1swfanalsirs1long.c1-checkpoint.ipynb deleted file mode 100644 index 108d3647..00000000 --- a/VAPs/quicklook/SWFLUXANAL/.ipynb_checkpoints/1swfanalsirs1long.c1-checkpoint.ipynb +++ /dev/null @@ -1,2654 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "70840257-70e4-45e2-b491-14bff5a257a3", - "metadata": {}, - "source": [ - "# 1SWFANALSIRS1LONG.C1 Plots\n", - "\n", - "[Click here](https://www.arm.gov/capabilities/vaps/swfluxanal) for more information about this vap." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "460fd89f-e034-452c-b837-f65c5958264f", - "metadata": {}, - "outputs": [], - "source": [ - "%matplotlib widget\n", - "import ipywidgets as widgets\n", - "\n", - "import matplotlib.pyplot as plt\n", - "import ipywidgets as widgets\n", - "import numpy as np\n", - "import pandas as pd\n", - "import os\n", - "from datetime import datetime\n", - "\n", - "import act\n", - "import xarray as xr\n", - "\n", - "# Data archive directory\n", - "DATA_DIR = r'/data/archive/'\n", - "\n", - "# Datastream info\n", - "DATASTREAM_NAME = '1swfanalsirs1long'\n", - "DATA_LEVEL = 'c1'\n", - "LOCATIONS = [{'end_date': '2015-05-18', 'facility': 'C1', 'site': 'sgp', 'start_date': '1997-03-25'}, {'end_date': '2011-10-15', 'facility': 'E10', 'site': 'sgp', 'start_date': '1997-02-16'}, {'end_date': '2015-05-18', 'facility': 'E11', 'site': 'sgp', 'start_date': '1995-09-26'}, {'end_date': '2015-05-26', 'facility': 'E12', 'site': 'sgp', 'start_date': '1996-01-21'}, {'end_date': '2015-05-18', 'facility': 'E13', 'site': 'sgp', 'start_date': '1994-01-07'}, {'end_date': '2015-05-18', 'facility': 'E15', 'site': 'sgp', 'start_date': '1994-03-31'}, {'end_date': '2011-11-09', 'facility': 'E16', 'site': 'sgp', 'start_date': '1995-09-22'}, {'end_date': '2009-11-06', 'facility': 'E18', 'site': 'sgp', 'start_date': '1996-06-20'}, {'end_date': '2011-05-21', 'facility': 'E19', 'site': 'sgp', 'start_date': '1998-07-21'}, {'end_date': '2009-05-07', 'facility': 'E1', 'site': 'sgp', 'start_date': '1995-11-16'}, {'end_date': '2011-11-14', 'facility': 'E20', 'site': 'sgp', 'start_date': '1995-04-02'}, {'end_date': '2015-05-11', 'facility': 'E21', 'site': 'sgp', 'start_date': '1999-09-13'}, {'end_date': '2009-11-29', 'facility': 'E22', 'site': 'sgp', 'start_date': '1995-11-09'}, {'end_date': '2009-11-06', 'facility': 'E24', 'site': 'sgp', 'start_date': '1995-11-08'}, {'end_date': '2002-04-03', 'facility': 'E25', 'site': 'sgp', 'start_date': '1997-11-19'}, {'end_date': '2009-07-15', 'facility': 'E27', 'site': 'sgp', 'start_date': '2003-05-16'}, {'end_date': '2009-10-18', 'facility': 'E2', 'site': 'sgp', 'start_date': '1996-04-02'}, {'end_date': '2015-05-12', 'facility': 'E31', 'site': 'sgp', 'start_date': '2011-10-13'}, {'end_date': '2015-05-18', 'facility': 'E32', 'site': 'sgp', 'start_date': '2012-02-05'}, {'end_date': '2015-05-18', 'facility': 'E33', 'site': 'sgp', 'start_date': '2011-08-26'}, {'end_date': '2015-05-26', 'facility': 'E34', 'site': 'sgp', 'start_date': '2011-09-04'}, {'end_date': '2015-05-18', 'facility': 'E35', 'site': 'sgp', 'start_date': '2011-10-06'}, {'end_date': '2015-05-18', 'facility': 'E36', 'site': 'sgp', 'start_date': '2011-09-29'}, {'end_date': '2015-05-18', 'facility': 'E37', 'site': 'sgp', 'start_date': '2011-09-30'}, {'end_date': '2015-05-27', 'facility': 'E38', 'site': 'sgp', 'start_date': '2011-09-05'}, {'end_date': '2009-08-30', 'facility': 'E3', 'site': 'sgp', 'start_date': '1996-03-07'}, {'end_date': '2011-09-25', 'facility': 'E4', 'site': 'sgp', 'start_date': '1996-03-07'}, {'end_date': '2009-10-31', 'facility': 'E5', 'site': 'sgp', 'start_date': '1996-06-17'}, {'end_date': '2011-10-15', 'facility': 'E6', 'site': 'sgp', 'start_date': '1996-03-07'}, {'end_date': '2011-11-12', 'facility': 'E7', 'site': 'sgp', 'start_date': '1995-10-20'}, {'end_date': '2009-11-04', 'facility': 'E8', 'site': 'sgp', 'start_date': '1995-09-29'}, {'end_date': '2015-05-26', 'facility': 'E9', 'site': 'sgp', 'start_date': '1994-01-19'}]" - ] - }, - { - "cell_type": "markdown", - "id": "9faaf875", - "metadata": {}, - "source": [ - "## Define site, facility, and date range" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "ac6764f5", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "The following locations and date ranges are available for this VAP:\n" - ] - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
sitefacilitystart_dateend_date
0sgpC11997-03-252015-05-18
1sgpE101997-02-162011-10-15
2sgpE111995-09-262015-05-18
3sgpE121996-01-212015-05-26
4sgpE131994-01-072015-05-18
5sgpE151994-03-312015-05-18
6sgpE161995-09-222011-11-09
7sgpE181996-06-202009-11-06
8sgpE191998-07-212011-05-21
9sgpE11995-11-162009-05-07
10sgpE201995-04-022011-11-14
11sgpE211999-09-132015-05-11
12sgpE221995-11-092009-11-29
13sgpE241995-11-082009-11-06
14sgpE251997-11-192002-04-03
15sgpE272003-05-162009-07-15
16sgpE21996-04-022009-10-18
17sgpE312011-10-132015-05-12
18sgpE322012-02-052015-05-18
19sgpE332011-08-262015-05-18
20sgpE342011-09-042015-05-26
21sgpE352011-10-062015-05-18
22sgpE362011-09-292015-05-18
23sgpE372011-09-302015-05-18
24sgpE382011-09-052015-05-27
25sgpE31996-03-072009-08-30
26sgpE41996-03-072011-09-25
27sgpE51996-06-172009-10-31
28sgpE61996-03-072011-10-15
29sgpE71995-10-202011-11-12
30sgpE81995-09-292009-11-04
31sgpE91994-01-192015-05-26
\n", - "
" - ], - "text/plain": [ - " site facility start_date end_date\n", - "0 sgp C1 1997-03-25 2015-05-18\n", - "1 sgp E10 1997-02-16 2011-10-15\n", - "2 sgp E11 1995-09-26 2015-05-18\n", - "3 sgp E12 1996-01-21 2015-05-26\n", - "4 sgp E13 1994-01-07 2015-05-18\n", - "5 sgp E15 1994-03-31 2015-05-18\n", - "6 sgp E16 1995-09-22 2011-11-09\n", - "7 sgp E18 1996-06-20 2009-11-06\n", - "8 sgp E19 1998-07-21 2011-05-21\n", - "9 sgp E1 1995-11-16 2009-05-07\n", - "10 sgp E20 1995-04-02 2011-11-14\n", - "11 sgp E21 1999-09-13 2015-05-11\n", - "12 sgp E22 1995-11-09 2009-11-29\n", - "13 sgp E24 1995-11-08 2009-11-06\n", - "14 sgp E25 1997-11-19 2002-04-03\n", - "15 sgp E27 2003-05-16 2009-07-15\n", - "16 sgp E2 1996-04-02 2009-10-18\n", - "17 sgp E31 2011-10-13 2015-05-12\n", - "18 sgp E32 2012-02-05 2015-05-18\n", - "19 sgp E33 2011-08-26 2015-05-18\n", - "20 sgp E34 2011-09-04 2015-05-26\n", - "21 sgp E35 2011-10-06 2015-05-18\n", - "22 sgp E36 2011-09-29 2015-05-18\n", - "23 sgp E37 2011-09-30 2015-05-18\n", - "24 sgp E38 2011-09-05 2015-05-27\n", - "25 sgp E3 1996-03-07 2009-08-30\n", - "26 sgp E4 1996-03-07 2011-09-25\n", - "27 sgp E5 1996-06-17 2009-10-31\n", - "28 sgp E6 1996-03-07 2011-10-15\n", - "29 sgp E7 1995-10-20 2011-11-12\n", - "30 sgp E8 1995-09-29 2009-11-04\n", - "31 sgp E9 1994-01-19 2015-05-26" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "print(\"The following locations and date ranges are available for this VAP:\")\n", - "display(pd.DataFrame(LOCATIONS, columns=['site', 'facility', 'start_date', 'end_date']))" - ] - }, - { - "cell_type": "markdown", - "id": "8d132223", - "metadata": {}, - "source": [ - "#### Define site, facility, and date range (date format: YYYY-MM-DD) using the variables below:" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "e563983a", - "metadata": {}, - "outputs": [], - "source": [ - "site_facility = ( 'sgp', 'C1' )\n", - "\n", - "date_start = '2015-05-15'\n", - "date_end = '2015-05-17'" - ] - }, - { - "cell_type": "markdown", - "id": "bccd3dfe-2f99-49a5-bace-ea37e8dd8fc3", - "metadata": {}, - "source": [ - "## Load data files\n", - "Load data files from /data/archive/" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "cb4b9a26-c574-49c0-a521-658fa553e39e", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'/data/archive/sgp/sgp1swfanalsirs1longC1.c1'" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Compile list of files\n", - "site, facility = site_facility\n", - "d_date_start = datetime.strptime(date_start, '%Y-%m-%d')\n", - "d_date_end = datetime.strptime(date_end, '%Y-%m-%d')\n", - "dir_path = os.path.join(DATA_DIR + site, site + DATASTREAM_NAME + facility + r'.' + DATA_LEVEL )\n", - "dir_path\n" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "6be8f3dc", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "['20150515', '20150516', '20150517']" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "from datetime import date, timedelta\n", - "import pandas as pd\n", - "\n", - "def get_ARM_formated_dates(start_date, end_date):\n", - " \"\"\"\n", - " Get a list of ARM conventional formated date lists, based on start_date and end_date(inclusive)\n", - " EXAMPLE:\n", - " get_ARM_formated_dates(start_date=\"20180219\", end_date=\"20180221\")\n", - " >> [\"20180219\", \"20180220\", \"20180221\"] \n", - " \"\"\"\n", - " \n", - " _start_date = pd.to_datetime(start_date)\n", - " _end_date = pd.to_datetime(end_date)\n", - " \n", - " delta = _end_date - _start_date # returns timedelta \n", - " dates = []\n", - "\n", - " for i in range(delta.days + 1):\n", - " day = _start_date + timedelta(days=i)\n", - " day_formated = day.strftime(format=\"%Y%m%d\")\n", - " dates.append(day_formated)\n", - " return dates\n", - "\n", - "\n", - "get_ARM_formated_dates(start_date=date_start, end_date=date_end)" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "51feea2e", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "['/data/archive/sgp/sgp1swfanalsirs1longC1.c1/sgp1swfanalsirs1longC1.c1.20150515.112900.cdf',\n", - " '/data/archive/sgp/sgp1swfanalsirs1longC1.c1/sgp1swfanalsirs1longC1.c1.20150516.112800.cdf',\n", - " '/data/archive/sgp/sgp1swfanalsirs1longC1.c1/sgp1swfanalsirs1longC1.c1.20150517.112700.cdf']" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Filter a list of files based on date pattern\n", - "import glob\n", - "dates = get_ARM_formated_dates(start_date=date_start, end_date=date_end)\n", - "files_filter = []\n", - "for date in dates:\n", - " files_filter += glob.glob(f'{dir_path}/*.{date}*.*')\n", - " files_filter\n", - "files_filter" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "id": "b0e5d0d7", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "89 files loaded\n" - ] - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "
<xarray.Dataset>\n",
-       "Dimensions:                          (time: 838)\n",
-       "Coordinates:\n",
-       "  * time                             (time) timedelta64[ns] 00:00:00 ... 13:5...\n",
-       "Data variables: (12/47)\n",
-       "    base_time                        object ...\n",
-       "    time_offset                      (time) timedelta64[ns] dask.array<chunksize=(838,), meta=np.ndarray>\n",
-       "    base_time_LST                    object ...\n",
-       "    time_offset_LST                  (time) timedelta64[ns] dask.array<chunksize=(838,), meta=np.ndarray>\n",
-       "    site                             |S64 ...\n",
-       "    coef_date                        float64 ...\n",
-       "    ...                               ...\n",
-       "    qc_difswfluxdn                   (time) int16 dask.array<chunksize=(838,), meta=np.ndarray>\n",
-       "    qc_dirswfluxdn                   (time) int16 dask.array<chunksize=(838,), meta=np.ndarray>\n",
-       "    qc_sswfluxdn                     (time) int16 dask.array<chunksize=(838,), meta=np.ndarray>\n",
-       "    lat                              float32 ...\n",
-       "    lon                              float32 ...\n",
-       "    alt                              float32 ...\n",
-       "Attributes: (12/14)\n",
-       "    Date:                      Sat Jun 20 17:08:21 GMT 2015\n",
-       "    Fitmode:                   01\n",
-       "    Version:                   $State: vap-swfanal1long-3.12-0.sol5_10$\n",
-       "    Number_Input_Platforms:    1\n",
-       "    Input_Platforms:           sgpsirsC1.b1\n",
-       "    Input_Platforms_Versions:  /usr/lib/ld.so.1\n",
-       "    ...                        ...\n",
-       "    comment:                   fitmode=01 indicates a daily fit, fitmode=00 i...\n",
-       "    _file_dates:               ['20150515']\n",
-       "    _file_times:               ['112900']\n",
-       "    datastream:                sgp1swfanalsirs1longC1.c1\n",
-       "    _datastream:               sgp1swfanalsirs1longC1.c1\n",
-       "    _arm_standards_flag:       1
" - ], - "text/plain": [ - "\n", - "Dimensions: (time: 838)\n", - "Coordinates:\n", - " * time (time) timedelta64[ns] 00:00:00 ... 13:5...\n", - "Data variables: (12/47)\n", - " base_time object ...\n", - " time_offset (time) timedelta64[ns] dask.array\n", - " base_time_LST object ...\n", - " time_offset_LST (time) timedelta64[ns] dask.array\n", - " site |S64 ...\n", - " coef_date float64 ...\n", - " ... ...\n", - " qc_difswfluxdn (time) int16 dask.array\n", - " qc_dirswfluxdn (time) int16 dask.array\n", - " qc_sswfluxdn (time) int16 dask.array\n", - " lat float32 ...\n", - " lon float32 ...\n", - " alt float32 ...\n", - "Attributes: (12/14)\n", - " Date: Sat Jun 20 17:08:21 GMT 2015\n", - " Fitmode: 01\n", - " Version: $State: vap-swfanal1long-3.12-0.sol5_10$\n", - " Number_Input_Platforms: 1\n", - " Input_Platforms: sgpsirsC1.b1\n", - " Input_Platforms_Versions: /usr/lib/ld.so.1\n", - " ... ...\n", - " comment: fitmode=01 indicates a daily fit, fitmode=00 i...\n", - " _file_dates: ['20150515']\n", - " _file_times: ['112900']\n", - " datastream: sgp1swfanalsirs1longC1.c1\n", - " _datastream: sgp1swfanalsirs1longC1.c1\n", - " _arm_standards_flag: 1" - ] - }, - "execution_count": 13, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Load files as a single dataset\n", - "files_list = files_filter [0]\n", - "ds = act.io.armfiles.read_netcdf(files_list)\n", - "ds.clean.cleanup()\n", - "print(f'{len(files_list)} files loaded')\n", - "ds\n" - ] - }, - { - "cell_type": "markdown", - "id": "4a551094-9ec0-4b64-b80a-9940573c2f50", - "metadata": {}, - "source": [ - "## Plot time series data\n", - "#### Define the list of variables to be plotted:" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "id": "d09b789e-84f1-4605-846b-a72c110c8048", - "metadata": {}, - "outputs": [], - "source": [ - "variables_to_plot = ['gswfluxdn_measured', 'gswfluxdn_clearskyfit', 'difswfluxdn_measured']" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "id": "3458fb08-035b-4898-9253-0a94e6f9c97b", - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/home/kefeimo/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/coding/variables.py:147: FutureWarning: elementwise comparison failed; returning scalar instead, but in the future will perform elementwise comparison\n", - " condition |= data == fv\n" - ] - }, - { - "ename": "OverflowError", - "evalue": "int too big to convert", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mOverflowError\u001b[0m Traceback (most recent call last)", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/IPython/core/formatters.py:972\u001b[0m, in \u001b[0;36mMimeBundleFormatter.__call__\u001b[0;34m(self, obj, include, exclude)\u001b[0m\n\u001b[1;32m 969\u001b[0m method \u001b[38;5;241m=\u001b[39m get_real_method(obj, \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mprint_method)\n\u001b[1;32m 971\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m method \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[0;32m--> 972\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mmethod\u001b[49m\u001b[43m(\u001b[49m\u001b[43minclude\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43minclude\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mexclude\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mexclude\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 973\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m\n\u001b[1;32m 974\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/ipympl/backend_nbagg.py:336\u001b[0m, in \u001b[0;36mCanvas._repr_mimebundle_\u001b[0;34m(self, **kwargs)\u001b[0m\n\u001b[1;32m 333\u001b[0m plaintext \u001b[38;5;241m=\u001b[39m plaintext[:\u001b[38;5;241m110\u001b[39m] \u001b[38;5;241m+\u001b[39m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124m…\u001b[39m\u001b[38;5;124m'\u001b[39m\n\u001b[1;32m 335\u001b[0m buf \u001b[38;5;241m=\u001b[39m io\u001b[38;5;241m.\u001b[39mBytesIO()\n\u001b[0;32m--> 336\u001b[0m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mfigure\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43msavefig\u001b[49m\u001b[43m(\u001b[49m\u001b[43mbuf\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43mformat\u001b[39;49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43mpng\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mdpi\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43mfigure\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m)\u001b[49m\n\u001b[1;32m 338\u001b[0m base64_image \u001b[38;5;241m=\u001b[39m b64encode(buf\u001b[38;5;241m.\u001b[39mgetvalue())\u001b[38;5;241m.\u001b[39mdecode(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mutf-8\u001b[39m\u001b[38;5;124m'\u001b[39m)\n\u001b[1;32m 339\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_data_url \u001b[38;5;241m=\u001b[39m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mdata:image/png;base64,\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mbase64_image\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m'\u001b[39m\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/matplotlib/figure.py:3343\u001b[0m, in \u001b[0;36mFigure.savefig\u001b[0;34m(self, fname, transparent, **kwargs)\u001b[0m\n\u001b[1;32m 3339\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m ax \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39maxes:\n\u001b[1;32m 3340\u001b[0m stack\u001b[38;5;241m.\u001b[39menter_context(\n\u001b[1;32m 3341\u001b[0m ax\u001b[38;5;241m.\u001b[39mpatch\u001b[38;5;241m.\u001b[39m_cm_set(facecolor\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mnone\u001b[39m\u001b[38;5;124m'\u001b[39m, edgecolor\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mnone\u001b[39m\u001b[38;5;124m'\u001b[39m))\n\u001b[0;32m-> 3343\u001b[0m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mcanvas\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mprint_figure\u001b[49m\u001b[43m(\u001b[49m\u001b[43mfname\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/matplotlib/backend_bases.py:2366\u001b[0m, in \u001b[0;36mFigureCanvasBase.print_figure\u001b[0;34m(self, filename, dpi, facecolor, edgecolor, orientation, format, bbox_inches, pad_inches, bbox_extra_artists, backend, **kwargs)\u001b[0m\n\u001b[1;32m 2362\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[1;32m 2363\u001b[0m \u001b[38;5;66;03m# _get_renderer may change the figure dpi (as vector formats\u001b[39;00m\n\u001b[1;32m 2364\u001b[0m \u001b[38;5;66;03m# force the figure dpi to 72), so we need to set it again here.\u001b[39;00m\n\u001b[1;32m 2365\u001b[0m \u001b[38;5;28;01mwith\u001b[39;00m cbook\u001b[38;5;241m.\u001b[39m_setattr_cm(\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mfigure, dpi\u001b[38;5;241m=\u001b[39mdpi):\n\u001b[0;32m-> 2366\u001b[0m result \u001b[38;5;241m=\u001b[39m \u001b[43mprint_method\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 2367\u001b[0m \u001b[43m \u001b[49m\u001b[43mfilename\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 2368\u001b[0m \u001b[43m \u001b[49m\u001b[43mfacecolor\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mfacecolor\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 2369\u001b[0m \u001b[43m \u001b[49m\u001b[43medgecolor\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43medgecolor\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 2370\u001b[0m \u001b[43m \u001b[49m\u001b[43morientation\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43morientation\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 2371\u001b[0m \u001b[43m \u001b[49m\u001b[43mbbox_inches_restore\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43m_bbox_inches_restore\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 2372\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 2373\u001b[0m \u001b[38;5;28;01mfinally\u001b[39;00m:\n\u001b[1;32m 2374\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m bbox_inches \u001b[38;5;129;01mand\u001b[39;00m restore_bbox:\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/matplotlib/backend_bases.py:2232\u001b[0m, in \u001b[0;36mFigureCanvasBase._switch_canvas_and_return_print_method..\u001b[0;34m(*args, **kwargs)\u001b[0m\n\u001b[1;32m 2228\u001b[0m optional_kws \u001b[38;5;241m=\u001b[39m { \u001b[38;5;66;03m# Passed by print_figure for other renderers.\u001b[39;00m\n\u001b[1;32m 2229\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mdpi\u001b[39m\u001b[38;5;124m\"\u001b[39m, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mfacecolor\u001b[39m\u001b[38;5;124m\"\u001b[39m, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124medgecolor\u001b[39m\u001b[38;5;124m\"\u001b[39m, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124morientation\u001b[39m\u001b[38;5;124m\"\u001b[39m,\n\u001b[1;32m 2230\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mbbox_inches_restore\u001b[39m\u001b[38;5;124m\"\u001b[39m}\n\u001b[1;32m 2231\u001b[0m skip \u001b[38;5;241m=\u001b[39m optional_kws \u001b[38;5;241m-\u001b[39m {\u001b[38;5;241m*\u001b[39minspect\u001b[38;5;241m.\u001b[39msignature(meth)\u001b[38;5;241m.\u001b[39mparameters}\n\u001b[0;32m-> 2232\u001b[0m print_method \u001b[38;5;241m=\u001b[39m functools\u001b[38;5;241m.\u001b[39mwraps(meth)(\u001b[38;5;28;01mlambda\u001b[39;00m \u001b[38;5;241m*\u001b[39margs, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs: \u001b[43mmeth\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 2233\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43m{\u001b[49m\u001b[43mk\u001b[49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mv\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mfor\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mk\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mv\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;129;43;01min\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mkwargs\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mitems\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mif\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mk\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;129;43;01mnot\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[38;5;129;43;01min\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mskip\u001b[49m\u001b[43m}\u001b[49m\u001b[43m)\u001b[49m)\n\u001b[1;32m 2234\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m: \u001b[38;5;66;03m# Let third-parties do as they see fit.\u001b[39;00m\n\u001b[1;32m 2235\u001b[0m print_method \u001b[38;5;241m=\u001b[39m meth\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/matplotlib/backends/backend_agg.py:509\u001b[0m, in \u001b[0;36mFigureCanvasAgg.print_png\u001b[0;34m(self, filename_or_obj, metadata, pil_kwargs)\u001b[0m\n\u001b[1;32m 462\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mprint_png\u001b[39m(\u001b[38;5;28mself\u001b[39m, filename_or_obj, \u001b[38;5;241m*\u001b[39m, metadata\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mNone\u001b[39;00m, pil_kwargs\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mNone\u001b[39;00m):\n\u001b[1;32m 463\u001b[0m \u001b[38;5;250m \u001b[39m\u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[1;32m 464\u001b[0m \u001b[38;5;124;03m Write the figure to a PNG file.\u001b[39;00m\n\u001b[1;32m 465\u001b[0m \n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 507\u001b[0m \u001b[38;5;124;03m *metadata*, including the default 'Software' key.\u001b[39;00m\n\u001b[1;32m 508\u001b[0m \u001b[38;5;124;03m \"\"\"\u001b[39;00m\n\u001b[0;32m--> 509\u001b[0m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_print_pil\u001b[49m\u001b[43m(\u001b[49m\u001b[43mfilename_or_obj\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mpng\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mpil_kwargs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mmetadata\u001b[49m\u001b[43m)\u001b[49m\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/matplotlib/backends/backend_agg.py:457\u001b[0m, in \u001b[0;36mFigureCanvasAgg._print_pil\u001b[0;34m(self, filename_or_obj, fmt, pil_kwargs, metadata)\u001b[0m\n\u001b[1;32m 452\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21m_print_pil\u001b[39m(\u001b[38;5;28mself\u001b[39m, filename_or_obj, fmt, pil_kwargs, metadata\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mNone\u001b[39;00m):\n\u001b[1;32m 453\u001b[0m \u001b[38;5;250m \u001b[39m\u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[1;32m 454\u001b[0m \u001b[38;5;124;03m Draw the canvas, then save it using `.image.imsave` (to which\u001b[39;00m\n\u001b[1;32m 455\u001b[0m \u001b[38;5;124;03m *pil_kwargs* and *metadata* are forwarded).\u001b[39;00m\n\u001b[1;32m 456\u001b[0m \u001b[38;5;124;03m \"\"\"\u001b[39;00m\n\u001b[0;32m--> 457\u001b[0m \u001b[43mFigureCanvasAgg\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mdraw\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m)\u001b[49m\n\u001b[1;32m 458\u001b[0m mpl\u001b[38;5;241m.\u001b[39mimage\u001b[38;5;241m.\u001b[39mimsave(\n\u001b[1;32m 459\u001b[0m filename_or_obj, \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mbuffer_rgba(), \u001b[38;5;28mformat\u001b[39m\u001b[38;5;241m=\u001b[39mfmt, origin\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mupper\u001b[39m\u001b[38;5;124m\"\u001b[39m,\n\u001b[1;32m 460\u001b[0m dpi\u001b[38;5;241m=\u001b[39m\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mfigure\u001b[38;5;241m.\u001b[39mdpi, metadata\u001b[38;5;241m=\u001b[39mmetadata, pil_kwargs\u001b[38;5;241m=\u001b[39mpil_kwargs)\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/matplotlib/backends/backend_agg.py:400\u001b[0m, in \u001b[0;36mFigureCanvasAgg.draw\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 396\u001b[0m \u001b[38;5;66;03m# Acquire a lock on the shared font cache.\u001b[39;00m\n\u001b[1;32m 397\u001b[0m \u001b[38;5;28;01mwith\u001b[39;00m RendererAgg\u001b[38;5;241m.\u001b[39mlock, \\\n\u001b[1;32m 398\u001b[0m (\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mtoolbar\u001b[38;5;241m.\u001b[39m_wait_cursor_for_draw_cm() \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mtoolbar\n\u001b[1;32m 399\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m nullcontext()):\n\u001b[0;32m--> 400\u001b[0m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mfigure\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mdraw\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mrenderer\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 401\u001b[0m \u001b[38;5;66;03m# A GUI class may be need to update a window using this draw, so\u001b[39;00m\n\u001b[1;32m 402\u001b[0m \u001b[38;5;66;03m# don't forget to call the superclass.\u001b[39;00m\n\u001b[1;32m 403\u001b[0m \u001b[38;5;28msuper\u001b[39m()\u001b[38;5;241m.\u001b[39mdraw()\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/matplotlib/artist.py:95\u001b[0m, in \u001b[0;36m_finalize_rasterization..draw_wrapper\u001b[0;34m(artist, renderer, *args, **kwargs)\u001b[0m\n\u001b[1;32m 93\u001b[0m \u001b[38;5;129m@wraps\u001b[39m(draw)\n\u001b[1;32m 94\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mdraw_wrapper\u001b[39m(artist, renderer, \u001b[38;5;241m*\u001b[39margs, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs):\n\u001b[0;32m---> 95\u001b[0m result \u001b[38;5;241m=\u001b[39m \u001b[43mdraw\u001b[49m\u001b[43m(\u001b[49m\u001b[43martist\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mrenderer\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 96\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m renderer\u001b[38;5;241m.\u001b[39m_rasterizing:\n\u001b[1;32m 97\u001b[0m renderer\u001b[38;5;241m.\u001b[39mstop_rasterizing()\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/matplotlib/artist.py:72\u001b[0m, in \u001b[0;36mallow_rasterization..draw_wrapper\u001b[0;34m(artist, renderer)\u001b[0m\n\u001b[1;32m 69\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m artist\u001b[38;5;241m.\u001b[39mget_agg_filter() \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[1;32m 70\u001b[0m renderer\u001b[38;5;241m.\u001b[39mstart_filter()\n\u001b[0;32m---> 72\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mdraw\u001b[49m\u001b[43m(\u001b[49m\u001b[43martist\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mrenderer\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 73\u001b[0m \u001b[38;5;28;01mfinally\u001b[39;00m:\n\u001b[1;32m 74\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m artist\u001b[38;5;241m.\u001b[39mget_agg_filter() \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/matplotlib/figure.py:3140\u001b[0m, in \u001b[0;36mFigure.draw\u001b[0;34m(self, renderer)\u001b[0m\n\u001b[1;32m 3137\u001b[0m \u001b[38;5;66;03m# ValueError can occur when resizing a window.\u001b[39;00m\n\u001b[1;32m 3139\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mpatch\u001b[38;5;241m.\u001b[39mdraw(renderer)\n\u001b[0;32m-> 3140\u001b[0m \u001b[43mmimage\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_draw_list_compositing_images\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 3141\u001b[0m \u001b[43m \u001b[49m\u001b[43mrenderer\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43martists\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43msuppressComposite\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 3143\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m sfig \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39msubfigs:\n\u001b[1;32m 3144\u001b[0m sfig\u001b[38;5;241m.\u001b[39mdraw(renderer)\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/matplotlib/image.py:131\u001b[0m, in \u001b[0;36m_draw_list_compositing_images\u001b[0;34m(renderer, parent, artists, suppress_composite)\u001b[0m\n\u001b[1;32m 129\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m not_composite \u001b[38;5;129;01mor\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m has_images:\n\u001b[1;32m 130\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m a \u001b[38;5;129;01min\u001b[39;00m artists:\n\u001b[0;32m--> 131\u001b[0m \u001b[43ma\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mdraw\u001b[49m\u001b[43m(\u001b[49m\u001b[43mrenderer\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 132\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m 133\u001b[0m \u001b[38;5;66;03m# Composite any adjacent images together\u001b[39;00m\n\u001b[1;32m 134\u001b[0m image_group \u001b[38;5;241m=\u001b[39m []\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/matplotlib/artist.py:72\u001b[0m, in \u001b[0;36mallow_rasterization..draw_wrapper\u001b[0;34m(artist, renderer)\u001b[0m\n\u001b[1;32m 69\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m artist\u001b[38;5;241m.\u001b[39mget_agg_filter() \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[1;32m 70\u001b[0m renderer\u001b[38;5;241m.\u001b[39mstart_filter()\n\u001b[0;32m---> 72\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mdraw\u001b[49m\u001b[43m(\u001b[49m\u001b[43martist\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mrenderer\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 73\u001b[0m \u001b[38;5;28;01mfinally\u001b[39;00m:\n\u001b[1;32m 74\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m artist\u001b[38;5;241m.\u001b[39mget_agg_filter() \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/matplotlib/axes/_base.py:3064\u001b[0m, in \u001b[0;36m_AxesBase.draw\u001b[0;34m(self, renderer)\u001b[0m\n\u001b[1;32m 3061\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m artists_rasterized:\n\u001b[1;32m 3062\u001b[0m _draw_rasterized(\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mfigure, artists_rasterized, renderer)\n\u001b[0;32m-> 3064\u001b[0m \u001b[43mmimage\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_draw_list_compositing_images\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 3065\u001b[0m \u001b[43m \u001b[49m\u001b[43mrenderer\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43martists\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mfigure\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43msuppressComposite\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 3067\u001b[0m renderer\u001b[38;5;241m.\u001b[39mclose_group(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124maxes\u001b[39m\u001b[38;5;124m'\u001b[39m)\n\u001b[1;32m 3068\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mstale \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mFalse\u001b[39;00m\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/matplotlib/image.py:131\u001b[0m, in \u001b[0;36m_draw_list_compositing_images\u001b[0;34m(renderer, parent, artists, suppress_composite)\u001b[0m\n\u001b[1;32m 129\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m not_composite \u001b[38;5;129;01mor\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m has_images:\n\u001b[1;32m 130\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m a \u001b[38;5;129;01min\u001b[39;00m artists:\n\u001b[0;32m--> 131\u001b[0m \u001b[43ma\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mdraw\u001b[49m\u001b[43m(\u001b[49m\u001b[43mrenderer\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 132\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m 133\u001b[0m \u001b[38;5;66;03m# Composite any adjacent images together\u001b[39;00m\n\u001b[1;32m 134\u001b[0m image_group \u001b[38;5;241m=\u001b[39m []\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/matplotlib/artist.py:72\u001b[0m, in \u001b[0;36mallow_rasterization..draw_wrapper\u001b[0;34m(artist, renderer)\u001b[0m\n\u001b[1;32m 69\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m artist\u001b[38;5;241m.\u001b[39mget_agg_filter() \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[1;32m 70\u001b[0m renderer\u001b[38;5;241m.\u001b[39mstart_filter()\n\u001b[0;32m---> 72\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mdraw\u001b[49m\u001b[43m(\u001b[49m\u001b[43martist\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mrenderer\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 73\u001b[0m \u001b[38;5;28;01mfinally\u001b[39;00m:\n\u001b[1;32m 74\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m artist\u001b[38;5;241m.\u001b[39mget_agg_filter() \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/matplotlib/axis.py:1376\u001b[0m, in \u001b[0;36mAxis.draw\u001b[0;34m(self, renderer, *args, **kwargs)\u001b[0m\n\u001b[1;32m 1373\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m\n\u001b[1;32m 1374\u001b[0m renderer\u001b[38;5;241m.\u001b[39mopen_group(\u001b[38;5;18m__name__\u001b[39m, gid\u001b[38;5;241m=\u001b[39m\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mget_gid())\n\u001b[0;32m-> 1376\u001b[0m ticks_to_draw \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_update_ticks\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 1377\u001b[0m tlb1, tlb2 \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_get_ticklabel_bboxes(ticks_to_draw, renderer)\n\u001b[1;32m 1379\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m tick \u001b[38;5;129;01min\u001b[39;00m ticks_to_draw:\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/matplotlib/axis.py:1263\u001b[0m, in \u001b[0;36mAxis._update_ticks\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 1258\u001b[0m \u001b[38;5;250m\u001b[39m\u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[1;32m 1259\u001b[0m \u001b[38;5;124;03mUpdate ticks (position and labels) using the current data interval of\u001b[39;00m\n\u001b[1;32m 1260\u001b[0m \u001b[38;5;124;03mthe axes. Return the list of ticks that will be drawn.\u001b[39;00m\n\u001b[1;32m 1261\u001b[0m \u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[1;32m 1262\u001b[0m major_locs \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mget_majorticklocs()\n\u001b[0;32m-> 1263\u001b[0m major_labels \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mmajor\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mformatter\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mformat_ticks\u001b[49m\u001b[43m(\u001b[49m\u001b[43mmajor_locs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 1264\u001b[0m major_ticks \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mget_major_ticks(\u001b[38;5;28mlen\u001b[39m(major_locs))\n\u001b[1;32m 1265\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mmajor\u001b[38;5;241m.\u001b[39mformatter\u001b[38;5;241m.\u001b[39mset_locs(major_locs)\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/matplotlib/ticker.py:218\u001b[0m, in \u001b[0;36mFormatter.format_ticks\u001b[0;34m(self, values)\u001b[0m\n\u001b[1;32m 216\u001b[0m \u001b[38;5;250m\u001b[39m\u001b[38;5;124;03m\"\"\"Return the tick labels for all the ticks at once.\"\"\"\u001b[39;00m\n\u001b[1;32m 217\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mset_locs(values)\n\u001b[0;32m--> 218\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m [\u001b[38;5;28mself\u001b[39m(value, i) \u001b[38;5;28;01mfor\u001b[39;00m i, value \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28menumerate\u001b[39m(values)]\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/matplotlib/ticker.py:218\u001b[0m, in \u001b[0;36m\u001b[0;34m(.0)\u001b[0m\n\u001b[1;32m 216\u001b[0m \u001b[38;5;250m\u001b[39m\u001b[38;5;124;03m\"\"\"Return the tick labels for all the ticks at once.\"\"\"\u001b[39;00m\n\u001b[1;32m 217\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mset_locs(values)\n\u001b[0;32m--> 218\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m [\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mvalue\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mi\u001b[49m\u001b[43m)\u001b[49m \u001b[38;5;28;01mfor\u001b[39;00m i, value \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28menumerate\u001b[39m(values)]\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/matplotlib/dates.py:651\u001b[0m, in \u001b[0;36mDateFormatter.__call__\u001b[0;34m(self, x, pos)\u001b[0m\n\u001b[1;32m 650\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21m__call__\u001b[39m(\u001b[38;5;28mself\u001b[39m, x, pos\u001b[38;5;241m=\u001b[39m\u001b[38;5;241m0\u001b[39m):\n\u001b[0;32m--> 651\u001b[0m result \u001b[38;5;241m=\u001b[39m \u001b[43mnum2date\u001b[49m\u001b[43m(\u001b[49m\u001b[43mx\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mtz\u001b[49m\u001b[43m)\u001b[49m\u001b[38;5;241m.\u001b[39mstrftime(\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mfmt)\n\u001b[1;32m 652\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m _wrap_in_tex(result) \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_usetex \u001b[38;5;28;01melse\u001b[39;00m result\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/matplotlib/dates.py:544\u001b[0m, in \u001b[0;36mnum2date\u001b[0;34m(x, tz)\u001b[0m\n\u001b[1;32m 518\u001b[0m \u001b[38;5;250m\u001b[39m\u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[1;32m 519\u001b[0m \u001b[38;5;124;03mConvert Matplotlib dates to `~datetime.datetime` objects.\u001b[39;00m\n\u001b[1;32m 520\u001b[0m \n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 541\u001b[0m \u001b[38;5;124;03mFor details, see the module docstring.\u001b[39;00m\n\u001b[1;32m 542\u001b[0m \u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[1;32m 543\u001b[0m tz \u001b[38;5;241m=\u001b[39m _get_tzinfo(tz)\n\u001b[0;32m--> 544\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43m_from_ordinalf_np_vectorized\u001b[49m\u001b[43m(\u001b[49m\u001b[43mx\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mtz\u001b[49m\u001b[43m)\u001b[49m\u001b[38;5;241m.\u001b[39mtolist()\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/numpy/lib/function_base.py:2329\u001b[0m, in \u001b[0;36mvectorize.__call__\u001b[0;34m(self, *args, **kwargs)\u001b[0m\n\u001b[1;32m 2326\u001b[0m vargs \u001b[38;5;241m=\u001b[39m [args[_i] \u001b[38;5;28;01mfor\u001b[39;00m _i \u001b[38;5;129;01min\u001b[39;00m inds]\n\u001b[1;32m 2327\u001b[0m vargs\u001b[38;5;241m.\u001b[39mextend([kwargs[_n] \u001b[38;5;28;01mfor\u001b[39;00m _n \u001b[38;5;129;01min\u001b[39;00m names])\n\u001b[0;32m-> 2329\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_vectorize_call\u001b[49m\u001b[43m(\u001b[49m\u001b[43mfunc\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mfunc\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43margs\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mvargs\u001b[49m\u001b[43m)\u001b[49m\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/numpy/lib/function_base.py:2412\u001b[0m, in \u001b[0;36mvectorize._vectorize_call\u001b[0;34m(self, func, args)\u001b[0m\n\u001b[1;32m 2409\u001b[0m \u001b[38;5;66;03m# Convert args to object arrays first\u001b[39;00m\n\u001b[1;32m 2410\u001b[0m inputs \u001b[38;5;241m=\u001b[39m [asanyarray(a, dtype\u001b[38;5;241m=\u001b[39m\u001b[38;5;28mobject\u001b[39m) \u001b[38;5;28;01mfor\u001b[39;00m a \u001b[38;5;129;01min\u001b[39;00m args]\n\u001b[0;32m-> 2412\u001b[0m outputs \u001b[38;5;241m=\u001b[39m \u001b[43mufunc\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43minputs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 2414\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m ufunc\u001b[38;5;241m.\u001b[39mnout \u001b[38;5;241m==\u001b[39m \u001b[38;5;241m1\u001b[39m:\n\u001b[1;32m 2415\u001b[0m res \u001b[38;5;241m=\u001b[39m asanyarray(outputs, dtype\u001b[38;5;241m=\u001b[39motypes[\u001b[38;5;241m0\u001b[39m])\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/matplotlib/dates.py:359\u001b[0m, in \u001b[0;36m_from_ordinalf\u001b[0;34m(x, tz)\u001b[0m\n\u001b[1;32m 346\u001b[0m \u001b[38;5;250m\u001b[39m\u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[1;32m 347\u001b[0m \u001b[38;5;124;03mConvert Gregorian float of the date, preserving hours, minutes,\u001b[39;00m\n\u001b[1;32m 348\u001b[0m \u001b[38;5;124;03mseconds and microseconds. Return value is a `.datetime`.\u001b[39;00m\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 353\u001b[0m \u001b[38;5;124;03m:rc:`timezone`.\u001b[39;00m\n\u001b[1;32m 354\u001b[0m \u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[1;32m 356\u001b[0m tz \u001b[38;5;241m=\u001b[39m _get_tzinfo(tz)\n\u001b[1;32m 358\u001b[0m dt \u001b[38;5;241m=\u001b[39m (np\u001b[38;5;241m.\u001b[39mdatetime64(get_epoch()) \u001b[38;5;241m+\u001b[39m\n\u001b[0;32m--> 359\u001b[0m \u001b[43mnp\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mtimedelta64\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mint\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mnp\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mround\u001b[49m\u001b[43m(\u001b[49m\u001b[43mx\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43m \u001b[49m\u001b[43mMUSECONDS_PER_DAY\u001b[49m\u001b[43m)\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43mus\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m)\u001b[49m)\n\u001b[1;32m 360\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m dt \u001b[38;5;241m<\u001b[39m np\u001b[38;5;241m.\u001b[39mdatetime64(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m0001-01-01\u001b[39m\u001b[38;5;124m'\u001b[39m) \u001b[38;5;129;01mor\u001b[39;00m dt \u001b[38;5;241m>\u001b[39m\u001b[38;5;241m=\u001b[39m np\u001b[38;5;241m.\u001b[39mdatetime64(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m10000-01-01\u001b[39m\u001b[38;5;124m'\u001b[39m):\n\u001b[1;32m 361\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mDate ordinal \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mx\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m converts to \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mdt\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m (using \u001b[39m\u001b[38;5;124m'\u001b[39m\n\u001b[1;32m 362\u001b[0m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mepoch \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mget_epoch()\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m), but Matplotlib dates must be \u001b[39m\u001b[38;5;124m'\u001b[39m\n\u001b[1;32m 363\u001b[0m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mbetween year 0001 and 9999.\u001b[39m\u001b[38;5;124m'\u001b[39m)\n", - "\u001b[0;31mOverflowError\u001b[0m: int too big to convert" - ] - }, - { - "data": { - "text/plain": [ - "Canvas(toolbar=Toolbar(toolitems=[('Home', 'Reset original view', 'home', 'home'), ('Back', 'Back to previous view', 'arrow-left', 'back'), ('Forward', 'Forward to next view', 'arrow-right', 'forward'), ('Pan', 'Left button pans, Right button zooms\\nx/y fixes axis, CTRL fixes aspect', 'arrows', 'pan'), ('Zoom', 'Zoom to rectangle\\nx/y fixes axis', 'square-o', 'zoom'), ('Download', 'Download plot', 'floppy-o', 'save_figure')]))" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "ts_display = act.plotting.TimeSeriesDisplay(ds)\n", - "ts_display.add_subplots((len(variables_to_plot),), figsize = (9.5,4*len(variables_to_plot)))\n", - "\n", - "for i,v in enumerate(variables_to_plot):\n", - " ts_ax = ts_display.plot(v, subplot_index=(i,), set_title=ds.variables[v].attrs['long_name'],)\n", - " ts_ax.grid()\n", - "\n", - "plt.show()\n" - ] - }, - { - "cell_type": "markdown", - "id": "194399aa-1907-452b-8ba9-bc31d7f60291", - "metadata": {}, - "source": [ - "## Quality check plots\n", - "#### Define variable for QC plot" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "id": "9a0ef63d-3eeb-48fc-a24a-43258b6134b8", - "metadata": {}, - "outputs": [], - "source": [ - "qc_variable = 'gswfluxdn'" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "id": "532663a3-4dc0-4497-bda8-018c5f91e1c4", - "metadata": {}, - "outputs": [ - { - "ename": "KeyError", - "evalue": "'gswfluxdn'", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mKeyError\u001b[0m Traceback (most recent call last)", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/core/dataset.py:1348\u001b[0m, in \u001b[0;36mDataset._construct_dataarray\u001b[0;34m(self, name)\u001b[0m\n\u001b[1;32m 1347\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m-> 1348\u001b[0m variable \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_variables\u001b[49m\u001b[43m[\u001b[49m\u001b[43mname\u001b[49m\u001b[43m]\u001b[49m\n\u001b[1;32m 1349\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mKeyError\u001b[39;00m:\n", - "\u001b[0;31mKeyError\u001b[0m: 'gswfluxdn'", - "\nDuring handling of the above exception, another exception occurred:\n", - "\u001b[0;31mKeyError\u001b[0m Traceback (most recent call last)", - "Cell \u001b[0;32mIn[12], line 7\u001b[0m\n\u001b[1;32m 5\u001b[0m qc_display \u001b[38;5;241m=\u001b[39m act\u001b[38;5;241m.\u001b[39mplotting\u001b[38;5;241m.\u001b[39mTimeSeriesDisplay(ds)\n\u001b[1;32m 6\u001b[0m qc_display\u001b[38;5;241m.\u001b[39madd_subplots((\u001b[38;5;241m2\u001b[39m,), figsize \u001b[38;5;241m=\u001b[39m (\u001b[38;5;241m9.5\u001b[39m,\u001b[38;5;241m10\u001b[39m))\n\u001b[0;32m----> 7\u001b[0m qc_ax \u001b[38;5;241m=\u001b[39m \u001b[43mqc_display\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mplot\u001b[49m\u001b[43m(\u001b[49m\u001b[43mqc_variable\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43msubplot_index\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m0\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mset_title\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mQC results on field: \u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m \u001b[49m\u001b[38;5;241;43m+\u001b[39;49m\u001b[43m \u001b[49m\u001b[43mqc_variable\u001b[49m\u001b[43m,\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 8\u001b[0m qc_ax\u001b[38;5;241m.\u001b[39mgrid()\n\u001b[1;32m 9\u001b[0m qc_display\u001b[38;5;241m.\u001b[39mqc_flag_block_plot(qc_variable, subplot_index\u001b[38;5;241m=\u001b[39m(\u001b[38;5;241m1\u001b[39m,))\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/act/plotting/timeseriesdisplay.py:418\u001b[0m, in \u001b[0;36mTimeSeriesDisplay.plot\u001b[0;34m(self, field, dsname, subplot_index, cmap, set_title, add_nan, day_night_background, invert_y_axis, abs_limits, time_rng, y_rng, use_var_for_y, set_shading, assessment_overplot, overplot_marker, overplot_behind, overplot_markersize, assessment_overplot_category, assessment_overplot_category_color, force_line_plot, labels, cbar_label, cbar_h_adjust, secondary_y, y_axis_flag_meanings, colorbar_labels, cb_friendly, **kwargs)\u001b[0m\n\u001b[1;32m 415\u001b[0m assessment_overplot_category_color[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mAcceptable\u001b[39m\u001b[38;5;124m'\u001b[39m] \u001b[38;5;241m=\u001b[39m (\u001b[38;5;241m0.0\u001b[39m, \u001b[38;5;241m0.4240129715562796\u001b[39m, \u001b[38;5;241m0.4240129715562796\u001b[39m),\n\u001b[1;32m 417\u001b[0m \u001b[38;5;66;03m# Get data and dimensions\u001b[39;00m\n\u001b[0;32m--> 418\u001b[0m data \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_obj\u001b[49m\u001b[43m[\u001b[49m\u001b[43mdsname\u001b[49m\u001b[43m]\u001b[49m\u001b[43m[\u001b[49m\u001b[43mfield\u001b[49m\u001b[43m]\u001b[49m\n\u001b[1;32m 419\u001b[0m dim \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mlist\u001b[39m(\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_obj[dsname][field]\u001b[38;5;241m.\u001b[39mdims)\n\u001b[1;32m 420\u001b[0m xdata \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_obj[dsname][dim[\u001b[38;5;241m0\u001b[39m]]\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/core/dataset.py:1439\u001b[0m, in \u001b[0;36mDataset.__getitem__\u001b[0;34m(self, key)\u001b[0m\n\u001b[1;32m 1437\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39misel(\u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkey)\n\u001b[1;32m 1438\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m utils\u001b[38;5;241m.\u001b[39mhashable(key):\n\u001b[0;32m-> 1439\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_construct_dataarray\u001b[49m\u001b[43m(\u001b[49m\u001b[43mkey\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 1440\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m utils\u001b[38;5;241m.\u001b[39miterable_of_hashable(key):\n\u001b[1;32m 1441\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_copy_listed(key)\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/core/dataset.py:1350\u001b[0m, in \u001b[0;36mDataset._construct_dataarray\u001b[0;34m(self, name)\u001b[0m\n\u001b[1;32m 1348\u001b[0m variable \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_variables[name]\n\u001b[1;32m 1349\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mKeyError\u001b[39;00m:\n\u001b[0;32m-> 1350\u001b[0m _, name, variable \u001b[38;5;241m=\u001b[39m \u001b[43m_get_virtual_variable\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_variables\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mname\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mdims\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 1352\u001b[0m needed_dims \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mset\u001b[39m(variable\u001b[38;5;241m.\u001b[39mdims)\n\u001b[1;32m 1354\u001b[0m coords: \u001b[38;5;28mdict\u001b[39m[Hashable, Variable] \u001b[38;5;241m=\u001b[39m {}\n", - "File \u001b[0;32m~/.conda/envs/jupyter-vaps/lib/python3.8/site-packages/xarray/core/dataset.py:186\u001b[0m, in \u001b[0;36m_get_virtual_variable\u001b[0;34m(variables, key, dim_sizes)\u001b[0m\n\u001b[1;32m 184\u001b[0m split_key \u001b[38;5;241m=\u001b[39m key\u001b[38;5;241m.\u001b[39msplit(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m.\u001b[39m\u001b[38;5;124m\"\u001b[39m, \u001b[38;5;241m1\u001b[39m)\n\u001b[1;32m 185\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mlen\u001b[39m(split_key) \u001b[38;5;241m!=\u001b[39m \u001b[38;5;241m2\u001b[39m:\n\u001b[0;32m--> 186\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mKeyError\u001b[39;00m(key)\n\u001b[1;32m 188\u001b[0m ref_name, var_name \u001b[38;5;241m=\u001b[39m split_key\n\u001b[1;32m 189\u001b[0m ref_var \u001b[38;5;241m=\u001b[39m variables[ref_name]\n", - "\u001b[0;31mKeyError\u001b[0m: 'gswfluxdn'" - ] - }, - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "fd79e0ba342048999f2bf386b4218b60", - "version_major": 2, - "version_minor": 0 - }, - "image/png": "iVBORw0KGgoAAAANSUhEUgAAA7YAAAPoCAYAAADqfmIfAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAA9hAAAPYQGoP6dpAABCA0lEQVR4nO3df2zV9b348Veh0Kr3toswKwgy3NXJLhm7lMAot1l0WgOGG5LdwOKNqBeTNdsuAa7egdzoICbN3c3MvU7BLYJmCbrGn/GPXkdzcy8/hJuMpiyLkLtFuBa2VlLMWtTdIvD5/mHo93YtDpCe9oWPR3L+OO99PvR1trfdefL5HE9ZURRFAAAAQFJjRnoAAAAA+CSELQAAAKkJWwAAAFITtgAAAKQmbAEAAEhN2AIAAJCasAUAACA1YQsAAEBqwhYAAIDUhC0AAACpCVsAAABSE7YAAACkJmwBAABITdgCAACQmrAFAAAgNWELAABAasIWAACA1IQtAAAAqQlbAAAAUhO2AAAApCZsAQAASE3YAgAAkJqwBQAAIDVhCwAAQGrCFgAAgNSELQAAAKkJWwAAAFITtgAAAKQmbAEAAEhN2AIAAJCasAUAACA1YQsAAEBqwhYAAIDUhC0AAACpCVsAAABSE7YAAACkJmwBAABITdgCAACQmrAFAAAgNWELAABAasIWAACA1IQtAAAAqQlbAAAAUhO2AAAApCZsAQAASE3YAgAAkJqwBQAAIDVhCwAAQGrCFgAAgNSELQAAAKkJWwAAAFITtgAAAKQmbAEAAEhN2AIAAJCasAUAACA1YQsAAEBqwhYAAIDUhC0AAACpCVsAAABSE7YAAACkJmwBAABITdgCAACQmrAFAAAgNWELAABAasIWAACA1IQtAAAAqQlbAAAAUhO2AAAApCZsAQAASE3YAgAAkJqwBQAAIDVhCwAAQGrCFgAAgNSELQAAAKkJWwAAAFITtgAAAKQmbAEAAEhN2AIAAJCasAUAACA1YQsAAEBqwhYAAIDUhC0AAACpCVsAAABSE7YAAACkJmwBAABITdgCAACQmrAFAAAgNWELAABAasIWAACA1IQtAAAAqQlbAAAAUhO2AAAApCZsAQAASE3YAgAAkJqwBQAAIDVhCwAAQGrCFgAAgNSELQAAAKkJWwAAAFITtgAAAKQmbAEAAEhN2AIAAJCasAUAACA1YQsAAEBqwhYAAIDUhC0AAACpCVsAAABSE7YAAACkJmwBAABITdgCAACQmrAFAAAgNWELAABAasIWAACA1IQtAAAAqQlbAAAAUhO2w2znzp2xePHimDx5cpSVlcWrr776R8/ZsWNH1NbWRmVlZdxwww3x1FNPDf+gAAAASQnbYfb+++/HrFmz4oknnjiv4w8fPhyLFi2K+vr6aG9vj4ceeihWrlwZL7300jBPCgAAkFNZURTFSA/xaVFWVhavvPJKLFmy5JzHfPe7343XXnstDh482L/W2NgYv/jFL2Lv3r0lmBIAACCX8pEegIH27t0bDQ0NA9buuOOO2LJlS3z44Ycxbty4Ic/r6+uLvr6+/udnzpyJd999NyZMmBBlZWXDOjMAAHzaFUURJ06ciMmTJ8eYMW6MLTVhO8p0dXVFTU3NgLWampo4depUdHd3x6RJk4Y8r6mpKTZs2FCKEQEAgHM4cuRITJkyZaTH+NQRtqPQH15hPXu3+MddeV23bl2sWbOm/3lPT09cf/31ceTIkaiqqhqeQQEAgIiI6O3tjalTp8af/umfjvQon0rCdpS59tpro6ura8DasWPHory8PCZMmHDO8yoqKqKiomLQelVVlbAFAIAS8THAkeHm71Fm/vz50draOmBt+/btMWfOnHN+vhYAAODTTNgOs/feey/2798f+/fvj4iPvs5n//790dHREREf3UK8fPny/uMbGxvj7bffjjVr1sTBgwdj69atsWXLlnjggQdGYnwAAIBRz63Iw2zfvn1xyy239D8/+znYe+65J5599tno7Ozsj9yIiOnTp0dLS0usXr06nnzyyZg8eXI8/vjj8fWvf73kswMAAGTge2wvU729vVFdXR09PT0+YwsAAMPM+++R5VZkAAAAUhO2AAAApCZsAQAASE3YAgAAkJqwBQAAIDVhCwAAQGrCFgAAgNSELQAAAKkJWwAAAFITtgAAAKQmbAEAAEhN2AIAAJCasAUAACA1YQsAAEBqwhYAAIDUhC0AAACpCVsAAABSE7YAAACkJmwBAABITdgCAACQmrAFAAAgNWELAABAasIWAACA1IQtAAAAqQlbAAAAUhO2AAAApCZsAQAASE3YAgAAkJqwBQAAIDVhCwAAQGrCFgAAgNSELQAAAKkJWwAAAFITtgAAAKQmbAEAAEhN2AIAAJCasAUAACA1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338/Zs2aFU888cR5He/9d4kVpDR37tyisbFxwNrNN99crF27dsjj/+Ef/qG4+eabB6x985vfLL7yla8M24zkcKF7aShf/OIXiw0bNlzq0UjmYvfSsmXLin/8x38sHnnkkWLWrFnDOCFZXOhe+rd/+7eiurq6OH78eCnGI5kL3U///M//XNxwww0D1h5//PFiypQpwzYj+URE8corr3zsMd5/l5YrtgmdPHky2traoqGhYcB6Q0ND7NmzZ8hz9u7dO+j4O+64I/bt2xcffvjhsM3K6HYxe+kPnTlzJk6cOBFXX331cIxIEhe7l5555pl466234pFHHhnuEUniYvbSa6+9FnPmzInvf//7cd1118VNN90UDzzwQPz+978vxciMYhezn+rq6uLo0aPR0tISRVHEO++8Ey+++GLceeedpRiZy4j336VVPtIDcOG6u7vj9OnTUVNTM2C9pqYmurq6hjynq6tryONPnToV3d3dMWnSpGGbl9HrYvbSH/rBD34Q77//fixdunQ4RiSJi9lLv/71r2Pt2rWxa9euKC/3f0d85GL20qFDh2L37t1RWVkZr7zySnR3d8e3vvWtePfdd33O9lPuYvZTXV1dbNu2LZYtWxb/+7//G6dOnYq/+qu/ih/+8IelGJnLiPffpeWKbWJlZWUDnhdFMWjtjx0/1DqfPhe6l856/vnn43vf+140NzfHNddcM1zjkcj57qXTp0/HXXfdFRs2bIibbrqpVOORyIX8Xjpz5kyUlZXFtm3bYu7cubFo0aJ47LHH4tlnn3XVloi4sP104MCBWLlyZTz88MPR1tYWr7/+ehw+fDgaGxtLMSqXGe+/S8dfkSc0ceLEGDt27KC/aTx27NigvxU669prrx3y+PLy8pgwYcKwzcrodjF76azm5uZYsWJFvPDCC3HbbbcN55gkcKF76cSJE7Fv375ob2+P73znOxHxUZwURRHl5eWxffv2uPXWW0syO6PLxfxemjRpUlx33XVRXV3dvzZjxowoiiKOHj0aN95447DOzOh1MfupqakpFixYEA8++GBERHzpS1+Kq666Kurr6+PRRx91lY3z5v13ablim9D48eOjtrY2WltbB6y3trZGXV3dkOfMnz9/0PHbt2+POXPmxLhx44ZtVka3i9lLER9dqb333nvjueee85kjIuLC91JVVVX88pe/jP379/c/Ghsb4wtf+ELs378/5s2bV6rRGWUu5vfSggUL4re//W289957/Wu/+tWvYsyYMTFlypRhnZfR7WL20wcffBBjxgx8izx27NiI+P9X2+B8eP9dYiP0L63iE/rpT39ajBs3rtiyZUtx4MCBYtWqVcVVV11V/M///E9RFEWxdu3a4u677+4//tChQ8WVV15ZrF69ujhw4ECxZcuWYty4ccWLL744Ui+BUeJC99Jzzz1XlJeXF08++WTR2dnZ//jd7343Ui+BUeJC99If8m9F5qwL3UsnTpwopkyZUvz1X/918eabbxY7duwobrzxxuL+++8fqZfAKHKh++mZZ54pysvLi02bNhVvvfVWsXv37mLOnDnF3LlzR+olMEqcOHGiaG9vL9rb24uIKB577LGivb29ePvtt4ui8P57pAnbxJ588sli2rRpxfjx44vZs2cXO3bs6P/P7rnnnuKrX/3qgOP/8z//s/iLv/iLYvz48cXnPve5YvPmzSWemNHqQvbSV7/61SIiBj3uueee0g/OqHOhv5f+L2HL/3Whe+ngwYPFbbfdVlxxxRXFlClTijVr1hQffPBBiadmtLrQ/fT4448XX/ziF4srrriimDRpUvE3f/M3xdGjR0s8NaPNf/zHf3zseyDvv0dWWVG4pwIAAIC8fMYWAACA1IQtAAAAqQlbAAAAUhO2AAAApCZsAQAASE3YAgAAkJqwBQAAIDVhCwAAQGrCFgAAgNSELQAAAKkJWwAAAFITtgAAAKQmbAEAAEhN2AIAAJCasAUAACA1YQsAAEBqwhYAAIDUhC0AAACpCVsAAABSE7YAAACkJmwBAABITdgCAACQmrAFAAAgNWELAABAasIWAACA1IQtAAAAqQlbAAAAUhO2AAAApCZsAQAASE3YAgAAkJqwBQAAIDVhCwAAQGrCFgAAgNSELQAAAKkJWwAAAFITtgAAAKQmbAEAAEhN2AIAAJCasAUAACA1YQsAAEBqwhYAAIDUhC0AAACpCVsAAABSE7YAAACkJmyH2c6dO2Px4sUxefLkKCsri1dfffWPnrNjx46ora2NysrKuOGGG+Kpp54a/kEBAACSErbD7P33349Zs2bFE088cV7HHz58OBYtWhT19fXR3t4eDz30UKxcuTJeeumlYZ4UAAAgp7KiKIqRHuLToqysLF555ZVYsmTJOY/57ne/G6+99locPHiwf62xsTF+8YtfxN69e0swJQAAQC7lIz0AA+3duzcaGhoGrN1xxx2xZcuW+PDDD2PcuHFDntfX1xd9fX39z8+cORPvvvtuTJgwIcrKyoZ1ZgAA+LQriiJOnDgRkydPjjFj3BhbasJ2lOnq6oqampoBazU1NXHq1Kno7u6OSZMmDXleU1NTbNiwoRQjAgAA53DkyJGYMmXKSI/xqSNsR6E/vMJ69m7xj7vyum7dulizZk3/856enrj++uvjyJEjUVVVNTyDAgAAERHR29sbU6dOjT/90z8d6VE+lYTtKHPttddGV1fXgLVjx45FeXl5TJgw4ZznVVRUREVFxaD1qqoqYQsAACXiY4Ajw83fo8z8+fOjtbV1wNr27dtjzpw55/x8LQAAwKeZsB1m7733Xuzfvz/2798fER99nc/+/fujo6MjIj66hXj58uX9xzc2Nsbbb78da9asiYMHD8bWrVtjy5Yt8cADD4zE+AAAAKOeW5GH2b59++KWW27pf372c7D33HNPPPvss9HZ2dkfuRER06dPj5aWlli9enU8+eSTMXny5Hj88cfj61//eslnBwAAyMD32F6ment7o7q6Onp6enzGFgAAhpn33yPLrcgAAACkJmwBAABITdgCAACQmrAFAAAgNWELAABAasIWAACA1IQtAAAAqQlbAAAAUhO2AAAApCZsAQAASE3YAgAAkJqwBQAAIDVhCwAAQGrCFgAAgNSELQAAAKkJWwAAAFITtgAAAKQmbAEAAEhN2AIAAJCasAUAACA1YQsAAEBqwhYAAIDUhC0AAACpCVsAAABSE7YAAACkJmwBAABITdgCAACQmrAFAAAgNWELAABAasIWAACA1IQtAAAAqQlbAAAAUhO2AAAApCZsAQAASE3YAgAAkJqwBQAAIDVhCwAAQGrCFgAAgNSELQAAAKkJWwAAAFITtgAAAKQmbAEAAEhN2AIAAJCasAUAACA1YQsAAEBqwhYAAIDUhC0AAACpCVsAAABSE7YAAACkJmwBAABITdgCAACQmrAFAAAgNWELAABAasIWAACA1IQtAAAAqQlbAAAAUhO2AAAApCZsAQAASE3YAgAAkJqwLZFNmzbF9OnTo7KyMmpra2PXrl0fe/y2bdti1qxZceWVV8akSZPivvvui+PHj5doWgAAgDyEbQk0NzfHqlWrYv369dHe3h719fWxcOHC6OjoGPL43bt3x/Lly2PFihXx5ptvxgsvvBA///nP4/777y/x5AAAAKOfsC2Bxx57LFasWBH3339/zJgxI/7lX/4lpk6dGps3bx7y+P/6r/+Kz33uc7Fy5cqYPn16/OVf/mV885vfjH379pV4cgAAgNFP2A6zkydPRltbWzQ0NAxYb2hoiD179gx5Tl1dXRw9ejRaWlqiKIp455134sUXX4w777zznD+nr68vent7BzwAAAA+DYTtMOvu7o7Tp09HTU3NgPWampro6uoa8py6urrYtm1bLFu2LMaPHx/XXnttfOYzn4kf/vCH5/w5TU1NUV1d3f+YOnXqJX0dAAAAo5WwLZGysrIBz4uiGLR21oEDB2LlypXx8MMPR1tbW7z++utx+PDhaGxsPOefv27duujp6el/HDly5JLODwAAMFqVj/QAl7uJEyfG2LFjB12dPXbs2KCruGc1NTXFggUL4sEHH4yIiC996Utx1VVXRX19fTz66KMxadKkQedUVFRERUXFpX8BAAAAo5wrtsNs/PjxUVtbG62trQPWW1tbo66ubshzPvjggxgzZuD/NGPHjo2Ij670AgAA8P8J2xJYs2ZNPP3007F169Y4ePBgrF69Ojo6OvpvLV63bl0sX768//jFixfHyy+/HJs3b45Dhw7FG2+8EStXroy5c+fG5MmTR+plAAAAjEpuRS6BZcuWxfHjx2Pjxo3R2dkZM2fOjJaWlpg2bVpERHR2dg74Ttt77703Tpw4EU888UT8/d//fXzmM5+JW2+9Nf7pn/5ppF4CAADAqFVWuLf1stTb2xvV1dXR09MTVVVVIz0OAABc1rz/HlluRQYAACA1YQsAAEBqwhYAAIDUhC0AAACpCVsAAABSE7YAAACkJmwBAABITdgCAACQmrAFAAAgNWELAABAasIWAACA1IQtAAAAqQlbAAAAUhO2AAAApCZsAQAASE3YAgAAkJqwBQAAIDVhCwAAQGrCFgAAgNSELQAAAKkJWwAAAFITtgAAAKQmbAEAAEhN2AIAAJCasAUAACA1YQsAAEBqwhYAAIDUhC0AAACpCVsAAABSE7YAAACkJmwBAABITdgCAACQmrAFAAAgNWELAABAasIWAACA1IQtAAAAqQlbAAAAUhO2AAAApCZsAQAASE3YAgAAkJqwBQAAIDVhCwAAQGrCFgAAgNSELQAAAKkJWwAAAFITtgAAAKQmbAEAAEhN2AIAAJCasAUAACA1YQsAAEBqwhYAAIDUhC0AAACpCVsAAABSE7YAAACkJmwBAABITdgCAACQmrAFAAAgNWELAABAasIWAACA1IRtiWzatCmmT58elZWVUVtbG7t27frY4/v6+mL9+vUxbdq0qKioiM9//vOxdevWEk0LAACQR/lID/Bp0NzcHKtWrYpNmzbFggUL4kc/+lEsXLgwDhw4ENdff/2Q5yxdujTeeeed2LJlS/zZn/1ZHDt2LE6dOlXiyQEAAEa/sqIoipEe4nI3b968mD17dmzevLl/bcaMGbFkyZJoamoadPzrr78e3/jGN+LQoUNx9dVXX9TP7O3tjerq6ujp6YmqqqqLnh0AAPjjvP8eWW5FHmYnT56Mtra2aGhoGLDe0NAQe/bsGfKc1157LebMmRPf//7347rrroubbropHnjggfj9739/zp/T19cXvb29Ax4AAACfBm5FHmbd3d1x+vTpqKmpGbBeU1MTXV1dQ55z6NCh2L17d1RWVsYrr7wS3d3d8a1vfSvefffdc37OtqmpKTZs2HDJ5wcAABjtXLEtkbKysgHPi6IYtHbWmTNnoqysLLZt2xZz586NRYsWxWOPPRbPPvvsOa/arlu3Lnp6evofR44cueSvAQAAYDRyxXaYTZw4McaOHTvo6uyxY8cGXcU9a9KkSXHddddFdXV1/9qMGTOiKIo4evRo3HjjjYPOqaioiIqKiks7PAAAQAKu2A6z8ePHR21tbbS2tg5Yb21tjbq6uiHPWbBgQfz2t7+N9957r3/tV7/6VYwZMyamTJkyrPMCAABkI2xLYM2aNfH000/H1q1b4+DBg7F69ero6OiIxsbGiPjoNuLly5f3H3/XXXfFhAkT4r777osDBw7Ezp0748EHH4y//du/jSuuuGKkXgYAAMCo5FbkEli2bFkcP348Nm7cGJ2dnTFz5sxoaWmJadOmRUREZ2dndHR09B//J3/yJ9Ha2hp/93d/F3PmzIkJEybE0qVL49FHHx2plwAAADBq+R7by5Tv0QIAgNLx/ntkuRUZAACA1IQtAAAAqQlbAAAAUhO2AAAApCZsAQAASE3YAgAAkJqwBQAAIDVhCwAAQGrCFgAAgNSELQAAAKkJWwAAAFITtgAAAKQmbAEAAEhN2AIAAJCasAUAACA1YQsAAEBqwhYAAIDUhC0AAACpCVsAAABSE7YAAACkJmwBAABITdgCAACQmrAFAAAgNWELAABAasIWAACA1IQtAAAAqQlbAAAAUhO2AAAApCZsAQAASE3YAgAAkJqwBQAAIDVhCwAAQGrCFgAAgNSELQAAAKkJWwAAAFITtgAAAKQmbAEAAEhN2AIAAJCasAUAACA1YQsAAEBqwhYAAIDUhC0AAACpCVsAAABSE7YAAACkJmwBAABITdgCAACQmrAFAAAgNWELAABAasIWAACA1IQtAAAAqQlbAAAAUhO2AAAApCZsAQAASE3YAgAAkJqwBQAAIDVhCwAAQGrCFgAAgNSELQAAAKkJWwAAAFITtiWyadOmmD59elRWVkZtbW3s2rXrvM574403ory8PL785S8P74AAAABJCdsSaG5ujlWrVsX69eujvb096uvrY+HChdHR0fGx5/X09MTy5cvja1/7WokmBQAAyKesKIpipIe43M2bNy9mz54dmzdv7l+bMWNGLFmyJJqams553je+8Y248cYbY+zYsfHqq6/G/v37z/tn9vb2RnV1dfT09ERVVdUnGR8AAPgjvP8eWa7YDrOTJ09GW1tbNDQ0DFhvaGiIPXv2nPO8Z555Jt5666145JFHzuvn9PX1RW9v74AHAADAp4GwHWbd3d1x+vTpqKmpGbBeU1MTXV1dQ57z61//OtauXRvbtm2L8vLy8/o5TU1NUV1d3f+YOnXqJ54dAAAgA2FbImVlZQOeF0UxaC0i4vTp03HXXXfFhg0b4qabbjrvP3/dunXR09PT/zhy5MgnnhkAACCD87scyEWbOHFijB07dtDV2WPHjg26ihsRceLEidi3b1+0t7fHd77znYiIOHPmTBRFEeXl5bF9+/a49dZbB51XUVERFRUVw/MiAAAARjFXbIfZ+PHjo7a2NlpbWwest7a2Rl1d3aDjq6qq4pe//GXs37+//9HY2Bhf+MIXYv/+/TFv3rxSjQ4AAJCCK7YlsGbNmrj77rtjzpw5MX/+/Pjxj38cHR0d0djYGBEf3Ub8m9/8Jn7yk5/EmDFjYubMmQPOv+aaa6KysnLQOgAAAMK2JJYtWxbHjx+PjRs3RmdnZ8ycOTNaWlpi2rRpERHR2dn5R7/TFgAAgKH5HtvLlO/RAgCA0vH+e2T5jC0AAACpCVsAAABSE7YAAACkJmwBAABITdgCAACQmrAFAAAgNWELAABAasIWAACA1IQtAAAAqQlbAAAAUhO2AAAApCZsAQAASE3YAgAAkJqwBQAAIDVhCwAAQGrCFgAAgNSELQAAAKkJWwAAAFITtgAAAKQmbAEAAEhN2AIAAJCasAUAACA1YQsAAEBqwhYAAIDUhC0AAACpCVsAAABSE7YAAACkJmwBAABITdgCAACQmrAFAAAgNWELAABAasIWAACA1IQtAAAAqQlbAAAAUhO2AAAApCZsAQAASE3YAgAAkJqwBQAAIDVhCwAAQGrCFgAAgNSELQAAAKkJWwAAAFITtgAAAKQmbAEAAEhN2AIAAJCasAUAACA1YQsAAEBqwhYAAIDUhC0AAACpCVsAAABSE7YAAACkJmwBAABITdgCAACQmrAFAAAgNWELAABAasIWAACA1IQtAAAAqQlbAAAAUhO2AAAApCZsS2TTpk0xffr0qKysjNra2ti1a9c5j3355Zfj9ttvj89+9rNRVVUV8+fPj5/97GclnBYAACAPYVsCzc3NsWrVqli/fn20t7dHfX19LFy4MDo6OoY8fufOnXH77bdHS0tLtLW1xS233BKLFy+O9vb2Ek8OAAAw+pUVRVGM9BCXu3nz5sXs2bNj8+bN/WszZsyIJUuWRFNT03n9GX/+538ey5Yti4cffvi8ju/t7Y3q6uro6emJqqqqi5obAAA4P95/jyxXbIfZyZMno62tLRoaGgasNzQ0xJ49e87rzzhz5kycOHEirr766nMe09fXF729vQMeAAAAnwbCdph1d3fH6dOno6amZsB6TU1NdHV1ndef8YMf/CDef//9WLp06TmPaWpqiurq6v7H1KlTP9HcAAAAWQjbEikrKxvwvCiKQWtDef755+N73/teNDc3xzXXXHPO49atWxc9PT39jyNHjnzimQEAADIoH+kBLncTJ06MsWPHDro6e+zYsUFXcf9Qc3NzrFixIl544YW47bbbPvbYioqKqKio+MTzAgAAZOOK7TAbP3581NbWRmtr64D11tbWqKurO+d5zz//fNx7773x3HPPxZ133jncYwIAAKTlim0JrFmzJu6+++6YM2dOzJ8/P3784x9HR0dHNDY2RsRHtxH/5je/iZ/85CcR8VHULl++PP71X/81vvKVr/Rf7b3iiiuiurp6xF4HAADAaCRsS2DZsmVx/Pjx2LhxY3R2dsbMmTOjpaUlpk2bFhERnZ2dA77T9kc/+lGcOnUqvv3tb8e3v/3t/vV77rknnn322VKPDwAAMKr5HtvLlO/RAgCA0vH+e2T5jC0AAACpCVsAAABSE7YAAACkJmwBAABITdgCAACQmrAFAAAgNWELAABAasIWAACA1IQtAAAAqQlbAAAAUhO2AAAApCZsAQAASE3YAgAAkJqwBQAAIDVhCwAAQGrCFgAAgNSELQAAAKkJWwAAAFITtgAAAKQmbAEAAEhN2AIAAJCasAUAACA1YQsAAEBqwhYAAIDUhC0AAACpCVsAAABSE7YAAACkJmwBAABITdgCAACQmrAFAAAgNWELAABAasIWAACA1IQtAAAAqQlbAAAAUhO2AAAApCZsAQAASE3YAgAAkJqwBQAAIDVhCwAAQGrCFgAAgNSELQAAAKkJWwAAAFITtgAAAKQmbAEAAEhN2AIAAJCasAUAACA1YQsAAEBqwhYAAIDUhC0AAACpCVsAAABSE7YAAACkJmwBAABITdgCAACQmrAFAAAgNWELAABAasIWAACA1IQtAAAAqQlbAAAAUhO2AAAApCZsS2TTpk0xffr0qKysjNra2ti1a9fHHr9jx46ora2NysrKuOGGG+Kpp54q0aQAAAC5CNsSaG5ujlWrVsX69eujvb096uvrY+HChdHR0THk8YcPH45FixZFfX19tLe3x0MPPRQrV66Ml156qcSTAwAAjH5lRVEUIz3E5W7evHkxe/bs2Lx5c//ajBkzYsmSJdHU1DTo+O9+97vx2muvxcGDB/vXGhsb4xe/+EXs3bv3vH5mb29vVFdXR09PT1RVVX3yFwEAAJyT998jq3ykB7jcnTx5Mtra2mLt2rUD1hsaGmLPnj1DnrN3795oaGgYsHbHHXfEli1b4sMPP4xx48YNOqevry/6+vr6n/f09ETER/+AAQAAw+vs+27XDUeGsB1m3d3dcfr06aipqRmwXlNTE11dXUOe09XVNeTxp06diu7u7pg0adKgc5qammLDhg2D1qdOnfoJpgcAAC7E8ePHo7q6eqTH+NQRtiVSVlY24HlRFIPW/tjxQ62ftW7dulizZk3/89/97ncxbdq06Ojo8A8Wn0hvb29MnTo1jhw54rYaPhF7iUvJfuJSsZe4VHp6euL666+Pq6++eqRH+VQStsNs4sSJMXbs2EFXZ48dOzboquxZ11577ZDHl5eXx4QJE4Y8p6KiIioqKgatV1dX+yXNJVFVVWUvcUnYS1xK9hOXir3EpTJmjH8/70jw3/owGz9+fNTW1kZra+uA9dbW1qirqxvynPnz5w86fvv27TFnzpwhP18LAADwaSZsS2DNmjXx9NNPx9atW+PgwYOxevXq6OjoiMbGxoj46Dbi5cuX9x/f2NgYb7/9dqxZsyYOHjwYW7dujS1btsQDDzwwUi8BAABg1HIrcgksW7Ysjh8/Hhs3bozOzs6YOXNmtLS0xLRp0yIiorOzc8B32k6fPj1aWlpi9erV8eSTT8bkyZPj8ccfj69//evn/TMrKirikUceGfL2ZLgQ9hKXir3EpWQ/canYS1wq9tLI8j22AAAApOZWZAAAAFITtgAAAKQmbAEAAEhN2AIAAJCasE1s06ZNMX369KisrIza2trYtWvXxx6/Y8eOqK2tjcrKyrjhhhviqaeeKtGkjHYXspdefvnluP322+Ozn/1sVFVVxfz58+NnP/tZCadlNLvQ30tnvfHGG1FeXh5f/vKXh3dA0rjQvdTX1xfr16+PadOmRUVFRXz+85+PrVu3lmhaRrsL3U/btm2LWbNmxZVXXhmTJk2K++67L44fP16iaRmtdu7cGYsXL47JkydHWVlZvPrqq3/0HO+/S0fYJtXc3ByrVq2K9evXR3t7e9TX18fChQsHfG3Q/3X48OFYtGhR1NfXR3t7ezz00EOxcuXKeOmll0o8OaPNhe6lnTt3xu233x4tLS3R1tYWt9xySyxevDja29tLPDmjzYXupbN6enpi+fLl8bWvfa1EkzLaXcxeWrp0afz7v/97bNmyJf77v/87nn/++bj55ptLODWj1YXup927d8fy5ctjxYoV8eabb8YLL7wQP//5z+P+++8v8eSMNu+//37MmjUrnnjiifM63vvvEitIae7cuUVjY+OAtZtvvrlYu3btkMf/wz/8Q3HzzTcPWPvmN79ZfOUrXxm2GcnhQvfSUL74xS8WGzZsuNSjkczF7qVly5YV//iP/1g88sgjxaxZs4ZxQrK40L30b//2b0V1dXVx/PjxUoxHMhe6n/75n/+5uOGGGwasPf7448WUKVOGbUbyiYjilVde+dhjvP8uLVdsEzp58mS0tbVFQ0PDgPWGhobYs2fPkOfs3bt30PF33HFH7Nu3Lz788MNhm5XR7WL20h86c+ZMnDhxIq6++urhGJEkLnYvPfPMM/HWW2/FI488MtwjksTF7KXXXnst5syZE9///vfjuuuui5tuuikeeOCB+P3vf1+KkRnFLmY/1dXVxdGjR6OlpSWKooh33nknXnzxxbjzzjtLMTKXEe+/S6t8pAfgwnV3d8fp06ejpqZmwHpNTU10dXUNeU5XV9eQx586dSq6u7tj0qRJwzYvo9fF7KU/9IMf/CDef//9WLp06XCMSBIXs5d+/etfx9q1a2PXrl1RXu7/jvjIxeylQ4cOxe7du6OysjJeeeWV6O7ujm9961vx7rvv+pztp9zF7Ke6urrYtm1bLFu2LP73f/83Tp06FX/1V38VP/zhD0sxMpcR779LyxXbxMrKygY8L4pi0NofO36odT59LnQvnfX888/H9773vWhubo5rrrlmuMYjkfPdS6dPn4677rorNmzYEDfddFOpxiORC/m9dObMmSgrK4tt27bF3LlzY9GiRfHYY4/Fs88+66otEXFh++nAgQOxcuXKePjhh6OtrS1ef/31OHz4cDQ2NpZiVC4z3n+Xjr8iT2jixIkxduzYQX/TeOzYsUF/K3TWtddeO+Tx5eXlMWHChGGbldHtYvbSWc3NzbFixYp44YUX4rbbbhvOMUngQvfSiRMnYt++fdHe3h7f+c53IuKjOCmKIsrLy2P79u1x6623lmR2RpeL+b00adKkuO6666K6urp/bcaMGVEURRw9ejRuvPHGYZ2Z0eti9lNTU1MsWLAgHnzwwYiI+NKXvhRXXXVV1NfXx6OPPuoqG+fN++/ScsU2ofHjx0dtbW20trYOWG9tbY26urohz5k/f/6g47dv3x5z5syJcePGDdusjG4Xs5ciPrpSe++998Zzzz3nM0dExIXvpaqqqvjlL38Z+/fv7380NjbGF77whdi/f3/MmzevVKMzylzM76UFCxbEb3/723jvvff61371q1/FmDFjYsqUKcM6L6PbxeynDz74IMaMGfgWeezYsRHx/6+2wfnw/rvERuhfWsUn9NOf/rQYN25csWXLluLAgQPFqlWriquuuqr4n//5n6IoimLt2rXF3Xff3X/8oUOHiiuvvLJYvXp1ceDAgWLLli3FuHHjihdffHGkXgKjxIXupeeee64oLy8vnnzyyaKzs7P/8bvf/W6kXgKjxIXupT/k34rMWRe6l06cOFFMmTKl+Ou//uvizTffLHbs2FHceOONxf333z9SL4FR5EL30zPPPFOUl5cXmzZtKt56661i9+7dxZw5c4q5c+eO1EtglDhx4kTR3t5etLe3FxFRPPbYY0V7e3vx9ttvF0Xh/fdIE7aJPfnkk8W0adOK8ePHF7Nnzy527NjR/5/dc889xVe/+tUBx//nf/5n8Rd/8RfF+PHji8997nPF5s2bSzwxo9WF7KWvfvWrRUQMetxzzz2lH5xR50J/L/1fwpb/60L30sGDB4vbbrutuOKKK4opU6YUa9asKT744IMST81odaH76fHHHy+++MUvFldccUUxadKk4m/+5m+Ko0ePlnhqRpv/+I//+Nj3QN5/j6yyonBPBQAAAHn5jC0AAACpCVsAAABSE7YAAACkJmwBAABITdgCAACQmrAFAAAgNWELAABAasIWAACA1IQtAAAAqQlbAAAAUhO2AAAApCZsAQAASE3YAgAAkJqwBQAAIDVhCwAAQGrCFgAAgNSELQAAAKkJWwAAAFITtgAAAKQmbAEAAEhN2AIAAJCasAUAACA1YQsAAEBqwhYAAIDUhC0AAACpCVsAAABSE7YAAACkJmwBAABITdgCAACQmrAFAAAgNWELAABAasIWAACA1IQtAAAAqQlbAAAAUhO2AAAApCZsAQAASE3YAgAAkJqwBQAAIDVhCwAAQGrCFgAAgNSELQAAAKkJWwAAAFITtgAAAKQmbAEAAEhN2AIAAJCasAUAACA1YQsAAEBqwhYAAIDUhC0AAACpCVsAAABSE7YAAACkJmwBAABITdgCAACQmrAFAAAgNWELAABAasIWAACA1IQtAAAAqQlbAAAAUhO2AAAApCZsAQAASE3YAgAAkJqwBQAAIDVhCwAAQGrCFgAAgNSELQAAAKkJWwAAAFITtgAAAKQmbAEAAEhN2AIAAJCasAUAACA1YQsAAEBqwhYAAIDUhC0AAACpCVsAAABSE7YAAACkJmwBAABITdgCAACQmrAFAAAgNWELAABAasIWAACA1IQtAAAAqQlbAAAAUhO2AAAApCZsAQAASE3YAgAAkJqwBQAAIDVhCwAAQGrCFgAAgNT+H/olNl1oxjotAAAAAElFTkSuQmCC", - "text/html": [ - "\n", - "
\n", - "
\n", - " Figure\n", - "
\n", - " \n", - "
\n", - " " - ], - "text/plain": [ - "Canvas(toolbar=Toolbar(toolitems=[('Home', 'Reset original view', 'home', 'home'), ('Back', 'Back to previous …" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# QC Plot\n", - "if ('qc_'+qc_variable) in ds.variables:\n", - "\n", - " # Plot\n", - " qc_display = act.plotting.TimeSeriesDisplay(ds)\n", - " qc_display.add_subplots((2,), figsize = (9.5,10))\n", - " qc_ax = qc_display.plot(qc_variable, subplot_index=(0,), set_title=\"QC results on field: \" + qc_variable,)\n", - " qc_ax.grid()\n", - " qc_display.qc_flag_block_plot(qc_variable, subplot_index=(1,))\n", - "\n", - " plt.show()\n", - "else:\n", - " print(f'QC not available for the selected field: {qc_variable}')\n" - ] - }, - { - "cell_type": "markdown", - "id": "2308db16-f362-4033-a11a-c3e5e75ad9ba", - "metadata": {}, - "source": [ - "## Field selection dropdown menu\n", - "Select variable to be plotted from a dropdown menu" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d5792fa8-2253-441a-8ac6-1e07a0345031", - "metadata": {}, - "outputs": [], - "source": [ - "plt.ioff()\n", - "\n", - "# populate dropdown menu with available variables \n", - "available_variables = [v for v in ds.variables if not('time' in v or v.startswith('qc_') or v.startswith('source_')) and 'long_name' in ds.variables[v].attrs]\n", - "d_variable = 'gswfluxdn_measured'\n", - "dropdown = widgets.Dropdown(\n", - " options = [(ds.variables[v].attrs['long_name'], v) for v in available_variables],\n", - " value= d_variable,\n", - " description='Field:',\n", - " disabled=False,\n", - ")\n", - "dropdown.layout.margin = '0px 30% 0px 20%'\n", - "dropdown.layout.width = '50%'\n", - "\n", - "# set up display\n", - "i_display = act.plotting.TimeSeriesDisplay(ds)\n", - "i_display.add_subplots((1,), figsize = (9.5,5))\n", - "i_ax = i_display.plot(d_variable, subplot_index=(0,), set_title=ds.variables[d_variable].attrs['long_name'],)\n", - "i_ax.grid()\n", - "i_fig = i_display.fig\n", - "\n", - "# update plot callback function\n", - "def update_plot(change):\n", - " i_ax.cla()\n", - " i_ax_new = i_display.plot(change.new, subplot_index=(0,), set_title=ds.variables[change.new].attrs['long_name'],)\n", - " i_ax_new.grid()\n", - " i_fig.canvas.draw()\n", - " i_fig.canvas.flush_events()\n", - "\n", - "dropdown.observe(update_plot, names='value')\n", - "\n", - "widgets.AppLayout(\n", - " header=dropdown,\n", - " center=i_fig.canvas,\n", - " pane_heights=[1, 6,1]\n", - ")\n" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.16" - }, - "widgets": { - "application/vnd.jupyter.widget-state+json": { - "state": {}, - "version_major": 2, - "version_minor": 0 - } - } - }, - "nbformat": 4, - "nbformat_minor": 5 -}