<jupyter_start><jupyter_text>Python: f-strings and Q&A MEOPAR ATM 2020-10-19<NAME>, UBC and 43ravens[<EMAIL>](mailto:<EMAIL>)Twitter: [@dlatornell](https://twitter.com/dlatornell)  Learning Goals* Explain what a Python f-string is* Write f-strings* Use f-strings as an aid to debug Python code* Format variable values in f-strings* Use f-strings to compose structured file paths/names  Combining Words and Variable Values in Strings String concatenation with a plus-sign seems like the simplest thing that might work:<jupyter_code>name = "Doug"
print("Hello " + name)<jupyter_output>Hello Doug<jupyter_text>Now, try it with a number:<jupyter_code>a = 43
print("The value of a is " + a)<jupyter_output><empty_output><jupyter_text>Various solutions to that problem:<jupyter_code>print("The value of a is " + str(a))
print("The value of a is %s" % a)
print("The value of a is {a}".format(a=a))<jupyter_output>The value of a is 43<jupyter_text>Python 3.6 brought a better way...  "Formatted String Literals" or f-strings<jupyter_code>print(f"Hello {name}")<jupyter_output>Hello Doug<jupyter_text>* Put an `f` (lowercase or uppercase, your choice) in front of the opening quote of the string* Enclose variable names in braces (`{}`)  Live-coding...<jupyter_code>print(f"The value of a is {a}")
a = 43
b = 71
print(f"a + b = {a + b}")<jupyter_output>a + b = 114<jupyter_text>Slides...  Learning Goals* ✅ Explain what a Python f-string is* ✅ Write f-strings* Use f-strings as an aid to debug Python code* Format variable values in f-strings* Use f-strings to compose structured file paths/names  f-strings and Debugging* A basic technique for debugging code in any language is to add print statements to  the code to trace its execution and show values of variables* Sometimes all you need is `print("here")` to tell you that a particular piece of code  is being executed* Sometimes you want to see the value of a variable: `print(f"var={var}")`  Live-coding... Imagine that we have an ocean model that includes heat transfer between the oceanand the atmosphere.We notice in our model output that the ocean temperature doesn't seem to be changingas quickly as we expect:it doesn't warm up in the summer, nor cool down in the winter.After some tracing through the code with `print()` statements to help us figure outwhat is going on,we reach a function that calculates the radiative component of the heat flux betweenthe atmosphere and the ocean.A really simple version of that function might be:<jupyter_code>def calc_rad_flux(temp_atmos, temp_ocean):
    # Stefan-Boltzmann constant [W m^2 K^-4]
    sigma = 5.670_374_419e-8
    rad_flux = sigma * (temp_atmos**4 - temp_ocean**4)
    return rad_flux
temp_atmos = 25.2
temp_ocean = 10.1
rad_flux = calc_rad_flux(temp_atmos, temp_ocean)
print(f"rad_flux = {rad_flux}")<jupyter_output>rad_flux = 0.022277186456082308<jupyter_text>Add debugging print statements to see the values of atmosphere and oceantemperatures that are actually being used to calculate the flux:<jupyter_code>def calc_rad_flux(temp_atmos, temp_ocean):
    # Stefan-Boltzmann constant [W m^2 K^-4]
    sigma = 5.670_374_419e-8
    print(f"temp_atmos = {temp_atmos}")
    print(f"temp_ocean = {temp_ocean}")
    rad_flux = sigma * (temp_atmos**4 - temp_ocean**4)
    return rad_flux
rad_flux = calc_rad_flux(temp_atmos, temp_ocean)
print(f"rad_flux = {rad_flux}")<jupyter_output>temp_atmos = 25.2
temp_ocean = 10.1
rad_flux = 0.022277186456082308<jupyter_text>And then it dawns on us:The temperatures have to be in Kelvin, not Celcius!So,fix the bug by adding 273.15 to the temperatures<jupyter_code>def calc_rad_flux(temp_atmos, temp_ocean):
    # Stefan-Boltzmann constant [W m^2 K^-4]
    sigma = 5.670_374_419e-8
    rad_flux = sigma * ((temp_atmos + 273.15)**4 - (temp_ocean + 273.15)**4)
    return rad_flux
rad_flux = calc_rad_flux(temp_atmos, temp_ocean)
print(f"rad_flux = {rad_flux}")<jupyter_output>rad_flux = 84.28000023251099<jupyter_text>The pattern of showing a variable name and its value is so common that Python 3.8added it as a new feature of f-strings - just put an equals sign (`=`) after the variable name:<jupyter_code>print(f"{a=}")
print(f"{a = }")<jupyter_output>a=43
a = 43<jupyter_text>Slides... Add `print()` statements to show variable names and their values to help understand code:<jupyter_code>temp_atmos = 15.2
print(f"temp_atmos = {temp_atmos}")<jupyter_output>temp_atmos = 15.2<jupyter_text>or, in Python>=3.8:<jupyter_code>print(f"{temp_atmos = }")<jupyter_output>temp_atmos = 15.2<jupyter_text>Learning Goals* ✅ Explain what a Python f-string is* ✅ Write f-strings* ✅ Use f-strings as an aid to debug Python code* Format variable values in f-strings* Use f-strings to compose structured file paths/names  Format Variable Values in f-strings  Live-coding...<jupyter_code>sigma = 5.670_374_419e-8
print(f"{sigma = }")
print(f"{sigma = :f}")
print(f"{sigma = :0.20f}")
print(f"{sigma = :1.4e}")
print(f"{a=:0.3f}")
print(f"{a=:04d}")<jupyter_output>a=0043<jupyter_text>Slides...<jupyter_code>sigma = 5.670_374_419e-8

print(f"{sigma = :0.20f}")

print(f"{sigma = :1.4e}")

print(f"{a=:0.3f}")

print(f"{a=:04d}")<jupyter_output>sigma = 0.00000005670374419000
sigma = 5.6704e-08
a=43.000
a=0043<jupyter_text>Format Specification Mini-Language:https://docs.python.org/3.8/library/string.htmlformatspec  Learning Goals* ✅ Explain what a Python f-string is* ✅ Write f-strings* ✅ Use f-strings as an aid to debug Python code* ✅ Format variable values in f-strings* Use f-strings to compose structured file paths/names  Use f-strings to Compose Structured File Paths/NamesLarge volumes of data like model output and ocean observations (e.g. Argo floats)are often stored using structured file paths and/or names.Examples:* URL for ECCC HRDPS forecast file: https://dd.weather.gc.ca/model_hrdps/west/grib2/12/017/CMC_hrdps_west_APCP_SFC_0_ps2.5km_2020101812_P017-00.grib2 * File path/name for UBC SalishSeaCast NEMO model output file:  `/results2/SalishSea/nowcast-green.201905/18oct20/SalishSea_1h_20201018_20201018_grid_T.nc`  Live-coding...<jupyter_code>import datetime
day = datetime.date(2020, 10, 18)
print(f"/results2/SalishSea/nowcast-green.201905/18oct20/SalishSea_1h_{day:%Y%m%d}_{day:%Y%m%d}_grid_T.nc")
ddmmmyy = f"{day:%d%b%y}".lower()
print(f"/results2/SalishSea/nowcast-green.201905/{ddmmmyy}/SalishSea_1h_{day:%Y%m%d}_{day:%Y%m%d}_grid_T.nc")
for day_num in range(1, 11):
    day = datetime.date(2020, 10, day_num)
    ddmmmyy = f"{day:%d%b%y}".lower()
    grid_T_path = f"/results2/SalishSea/nowcast-green.201905/{ddmmmyy}/SalishSea_1h_{day:%Y%m%d}_{day:%Y%m%d}_grid_T.nc"
    print(grid_T_path)
with xarray.open_dataset(grid_T_path) as grid_T:
    print(grid_T.data_vars)<jupyter_output><empty_output><jupyter_text>Slides...<jupyter_code>import datetime

for day_num in range(1, 11):
    day = datetime.date(2020, 10, day_num)
    ddmmmyy = f"{day:%d%b%y}".lower()
    grid_T_path = f"/results2/SalishSea/nowcast-green.201905/{ddmmmyy}/SalishSea_1h_{day:%Y%m%d}_{day:%Y%m%d}_grid_T.nc"
    print(grid_T_path)<jupyter_output>/results2/SalishSea/nowcast-green.201905/01oct20/SalishSea_1h_20201001_20201001_grid_T.nc
/results2/SalishSea/nowcast-green.201905/02oct20/SalishSea_1h_20201002_20201002_grid_T.nc
/results2/SalishSea/nowcast-green.201905/03oct20/SalishSea_1h_20201003_20201003_grid_T.nc
/results2/SalishSea/nowcast-green.201905/04oct20/SalishSea_1h_20201004_20201004_grid_T.nc
/results2/SalishSea/nowcast-green.201905/05oct20/SalishSea_1h_20201005_20201005_grid_T.nc
/results2/SalishSea/nowcast-green.201905/06oct20/SalishSea_1h_20201006_20201006_grid_T.nc
/results2/SalishSea/nowcast-green.201905/07oct20/SalishSea_1h_20201007_20201007_grid_T.nc
/results2/SalishSea/nowcast-green.201905/08oct20/SalishSea_1h_20201008_20201008_grid_T.nc
/results2/SalishSea/nowcast-green.201905/09oct20/SalishSea_1h_20201009_20201009_grid_T.nc
/results2/SalishSea/nowcast-green.201905/10oct20/SalishSea_1h_20201010_20201010_grid_T.nc<jupyter_text>Date/time formatting directives: https://docs.python.org/3/library/time.htmltime.strftime  Live-coding...<jupyter_code>hrdps_url = "https://dd.weather.gc.ca/model_hrdps/west/grib2/12/017/CMC_hrdps_west_APCP_SFC_0_ps2.5km_2020101812_P017-00.grib2"
hrdps_gribs = "https://dd.weather.gc.ca/model_hrdps/west/grib2"
forecast = 12
hr = 17
day = datetime.date(2020, 10, 18)
var = "APCP_SFC_0"
hrdps_url = f"{hrdps_gribs}/{forecast}/{hr:03d}/CMC_hrdps_west_{var}_ps2.5km_{day:%y%m%d}{forecast}_P{hr:03d}-00.grib2"
print(hrdps_url)
hrdps_url_tmpl = "{hrdps_gribs}/{forecast}/{hr:03d}/CMC_hrdps_west_{var}_ps2.5km_{day:%y%m%d}{forecast}_P{hr:03d}-00.grib2"
print(
    hrdps_url_tmpl.format(
        hrdps_gribs=hrdps_gribs, forecast=forecast, hr=hr, var=var, day=day))<jupyter_output>https://dd.weather.gc.ca/model_hrdps/west/grib2/12/017/CMC_hrdps_west_APCP_SFC_0_ps2.5km_20101812_P017-00.grib2<jupyter_text>Slides...<jupyter_code>hrdps_gribs = "https://dd.weather.gc.ca/model_hrdps/west/grib2"
forecast = 12
hr = 17
day = datetime.date(2020, 10, 18)
var = "APCP_SFC_0"
hrdps_url = f"{hrdps_gribs}/{forecast}/{hr:03d}/CMC_hrdps_west_{var}_ps2.5km_{day:%y%m%d}{forecast}_P{hr:03d}-00.grib2"
print(hrdps_url)<jupyter_output>https://dd.weather.gc.ca/model_hrdps/west/grib2/12/017/CMC_hrdps_west_APCP_SFC_0_ps2.5km_20101812_P017-00.grib2
{'fraction_non_alphanumeric': 0.12440961786174323, 'fraction_numerical': 0.08276084156290253, 'mean_word_length': 5.973802395209581, 'pattern_counts': {'":': 0, '<': 57, '<?xml version=': 0, '>': 58, 'https://': 9, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 1, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '17', 'dir': 'jupyter-structured-clean-dedup', 'id': '246789', 'license': 'Apache-2.0', 'n_tokens_mistral': 4232, 'n_tokens_neox': 3691, 'n_words': 747, 'path': 'Python-f-strings-Slides.ipynb', 'repo_name': '43ravens/MEOPAR-ATM-2020-10-19', 'size': '9362'}