psyplot / psyplot

Python package for interactive data visualization
https://psyplot.github.io
73 stars 14 forks source link

lineplot : wrong ylabel ? #40

Closed captcha1 closed 2 years ago

captcha1 commented 2 years ago

Code Sample, a copy-pastable example if possible

import datetime , psyplot , matplotlib.pyplot as plt

nomads2 ='gefs/gefs%Y%m%d/gefs_pgrb2bp5_all_00z' # GFS Ensemble .5 Degree (Additional Parms) # XXX hack : H hardcoded
f0 = 'gustsfc' # ** surface wind speed (gust) [m/s] , nomads gefs-b
f0 = 'presmsl' # ** mean sea level pressure [pa] # nomads gefs-b

dt0 = datetime.datetime.utcnow() - datetime.timedelta(days=2)
ds0 = psyplot.data.open_dataset(dt0.strftime('https://nomads.ncep.noaa.gov/dods/' + nomads2))
line0 = ds0.psy.plot.lineplot(name = f0,
    ylabel="{desc}",
    title = nomads2.split('/')[0],
    lat = 2*(90 + 38), lon = 2*(360 - 122)
)
print('=== line0 : ', line0)
plt.show()

Problem description

I expect the ylabel to be "mean sea level pressure", but it's "ensemble member".

Also, it possible to change the scale on the yaxis to be hectopascal (not pascal) ?

Also, the "lat" & "lon" selects are indexes, not degrees. Is there a way to use degrees ?

Any other tips to make a nice ensemble meteogram are welcome ...

Thanks !

Output of psyplot -aV

psy_maps.plugin: requirements: cartopy: 0.19.0 version: 1.4.0 psy_reg.plugin: requirements: scipy: 1.6.2 statsmodels: 0.12.2 version: 1.4.0 psy_simple.plugin: version: 1.4.0 psyplot: requirements: matplotlib: 3.4.3 numpy: 1.20.1 pandas: 1.2.5 python: 3.9.7 (default, Aug 30 2021, 00:00:00) [GCC 11.2.1 20210728 (Red Hat 11.2.1-1)] xarray: 0.17.0 version: 1.4.0 psyplot_gui: requirements: pyqt: 5.15.0 qt: 5.15.2 qtconsole: 5.0.3 version: 1.4.0
captcha1 commented 2 years ago

Another idea : on the xaxis, is there a way to specify the "time zone" ?

Chilipp commented 2 years ago

hey @captcha1! thanks for this input, I'll have a look :smiley:

Another idea : on the xaxis, is there a way to specify the "time zone" ?

currently not I think. The formatting of time is using the standard python datetime formatting rules and I don't think that's possible. One option might be to convert the time variable in your netCDF file to the timezone that you want to show before you do the plotting.

captcha1 commented 2 years ago

Also, the "lat" & "lon" selects are indexes, not degrees. Is there a way to use degrees ?

I guess this is the biggest issue for me (I want to tinker w/ hrrr, nam, rap, sref meteograms).

Chilipp commented 2 years ago

alright, I had a look:

I expect the ylabel to be "mean sea level pressure", but it's "ensemble member".

yes, this is because you have an ens dimension in your variable. If you look into your print statement

>>> print('=== line0 : ', line0)
=== line0 :  psyplot.project.Project([arr0: psyplot.data.InteractiveList([    arr0: 2-dim DataArray of presmsl, with (ens, time)=(31, 65), lat=38.0, lon=238.0])])

you have a resulting 2D-array with shape (31, 65). psy-simple then assumes that the first dimension represents the uncertainty (see https://psyplot.github.io/examples/simple/example_line.html#Visualizing-uncertainties). It assumes, that your first slice with ens=0 represents the data (i.e. the expected value), ens=1 is the minimum and ens=2) the maximum of the uncertainty interval. Actually it should raise an error as your ens dimension is larger than three.

Anyway, you can solve this by simply adding ens=0 to your lineplot call.


Also, it possible to change the scale on the yaxis to be hectopascal (not pascal) ?

no, I did not implement unit handling. You can create a formatoption for this or do this in a preprocessing step, i.e. something like ds0["lev"] = ds0["lev"] / 100.


Also, the "lat" & "lon" selects are indexes, not degrees. Is there a way to use degrees ?

yes, as with any other plot methods, you can use method="nearest", e.g. you call might be

line0 = ds0.psy.plot.lineplot(name = f0,
    ylabel="{desc}",
    title = nomads2.split('/')[0],
    lat = 38, lon = 238, ens=1, method="nearest",
)

Any other tips to make a nice ensemble meteogram are welcome ...

I am not an expert on meteograms (nor am I a meterologist :wink:) but you could visualize all the indivdual ensemble members via

line0 = ds0.psy.plot.lineplot(name = f0,
    ylabel="{desc}",
    title = nomads2.split('/')[0], color="binary", legend=False,
    lat = 38, lon = 238, ens=list(ds0.ens), method="nearest",
)

image

or compute and visualize uncertainties via

ds0 = ds0.sel(lat = 38, lon = 238)
ds0[f0 + "_mean"] = ds0[f0].mean("ens")
ds0[f0 + "_mean"].attrs.update(ds0[f0].attrs)
ds0[f0 + "_std"] = ds0[f0].std("ens")
line0 = ds0.psy.plot.lineplot(name = [[[f0 + "_mean", f0 + "_std"]]],
    ylabel="{desc}",
    title = nomads2.split('/')[0]
)

or, if you prefer percentiles as confidence interval,

ds0[f0 + "_pctl5"] = ds0[f0].quantile(0.05, "ens")
ds0[f0 + "_pctl95"] = ds0[f0].quantile(0.95, "ens")
line0 = ds0.psy.plot.lineplot(name = [[[f0 + "_mean", f0 + "_pctl5", f0 + "_pctl95"]]],
    ylabel="{desc}",
    title = nomads2.split('/')[0],
)

image

Note that I run ds0 = ds0.sel(lat = 38, lon = 238) in the beginning to be more efficient (otherwise xarray loads the entire data from the remote location). One could also do this within psyplot, but then you should create an own formatoption and define an uncertainty plotter (i.e. you extend psyplot with the formatoption approach, see https://psyplot.github.io/examples/general/example_extending_psyplot.html#3.-The-formatoption-approach for instance).

captcha1 commented 2 years ago

I am not an expert on meteograms (nor am I a meterologist wink) but you could visualize all the indivdual ensemble members via

Awesome !

or compute and visualize uncertainties via

ds0 = ds0.sel(lat = 38, lon = 238)
ds0[f0 + "_mean"] = ds0[f0].mean("ens")
ds0[f0 + "_mean"].attrs.update(ds0[f0].attrs)
ds0[f0 + "_std"] = ds0[f0].std("ens")
line0 = ds0.psy.plot.lineplot(name = [[[f0 + "_mean", f0 + "_std"]]])

Excellent ... however, I noticed that some stats are already computed. eg in https://nomads.ncep.noaa.gov/dods/gens_bc , there are urls :

nomads2 =  'gens_bc/gens%Y%m%d/geavg_00z' # bias-corrected GEFS average fcst
nomads2 =  'gens_bc/gens%Y%m%d/gespr_00z' # bias-corrected GEFS fcst spread
nomads2 = 'gens_bc/gens%Y%m%d/ge10pt_00z' # bias-corrected GEFS 10th percentile fcst

... but each is a separate url / open_dataset . Is it possible to send lineplot mean & std from different datasets ?

Chilipp commented 2 years ago

hey @captcha1! You can just use the standard xarray utilities to combine the datasets. E.g.

f0 = "prmslmsl"

nomads2_avg = 'gens_bc/gens%Y%m%d/geavg_00z'
nomads2_spr =  'gens_bc/gens%Y%m%d/gespr_00z'
ds0 = psyplot.data.open_dataset(dt0.strftime('https://nomads.ncep.noaa.gov/dods/' + nomads2_avg))
ds0_spr = psyplot.data.open_dataset(dt0.strftime('https://nomads.ncep.noaa.gov/dods/' + nomads2_spr))

ds0[f0 + "_spr"] = ds0_spr[f0]

line0 = ds0.psy.plot.lineplot(name = [[[f0, f0 + "_spr"]]],
    ylabel="{desc}",
    title = nomads2.split('/')[0], color="binary", legend=False,
    lat = 38, lon = 238, ens=1, method="nearest",
)

(although the data in this file at (lat, lon) = (38, 238) is apparently all NaN).

Chilipp commented 2 years ago

@captcha1: can this issue be closed?

captcha1 commented 2 years ago

@captcha1: can this issue be closed?

yes, thanks