<jupyter_start><jupyter_text>Book 3 of 4: Working with Units Table of Contents1&nbsp;&nbsp;Book 3 of 4: Working with Units1.1&nbsp;&nbsp;Learning Objectives1.2&nbsp;&nbsp;Previously:1.3&nbsp;&nbsp;Checking Units Manually1.4&nbsp;&nbsp;Using cf_units1.4.1&nbsp;&nbsp; Task 1 - Perform unit conversion on a temperature value 1.5&nbsp;&nbsp;Using apply_ufunc1.5.1&nbsp;&nbsp;What is apply_ufunc?1.5.2&nbsp;&nbsp; Task 2 -  Write a function for unit conversion1.5.3&nbsp;&nbsp; Task 3 -  Call up your unit conversion function1.6&nbsp;&nbsp;Going Futher:  Learning Objectives- Use Xarray's metadata and the `cfunits` package to convert units of dataset variables. ----------------  Previously:- We imported the Xarray module and loaded our data.- We wrote a function to subselect by depth.Before continuing, make sure the correct Python environment is running in your notebook.  See the beginning of Notebook 01 for more detailed information.<jupyter_code>import xarray as xr

path = '../../../data/'
file = path + 'thetao_Omon_historical_GISS-E2-1-G_r1i1p1f1_gn_185001-185512.nc'
ds = xr.open_dataset(file)


def limit_depth_of_variables(level_bounds, temperature, depth_limit):
    level_bounds_limited = level_bounds.where(
        level_bounds < depth_limit, depth_limit)
    delta_level = abs(level_bounds_limited[:, 1] - level_bounds_limited[:, 0])

    delta_level_limited = delta_level.where(delta_level != 0, drop=True)
    temperature_limited = temperature.where(delta_level != 0, drop=True)

    return delta_level_limited, temperature_limited


delta_level_limited, temperature_limited = limit_depth_of_variables(
    ds['lev_bnds'], ds['thetao'], 50)<jupyter_output><empty_output><jupyter_text>----------------  Checking Units ManuallyIn an Xarray Dataset, units are stored as an *attribute* of a dataset variable.  You can access the units as follows:<jupyter_code>level_units = ds['thetao'].attrs['units']
level_units<jupyter_output><empty_output><jupyter_text>Say we wanted to convert all temperature values from degrees Celsius to Kelvins. First we apply the conversion, then we add our new units attribute.<jupyter_code>heat_in_Kelvins = ds['thetao'] + 273.15
heat_in_Kelvins.attrs['units'] = 'degK'
heat_in_Kelvins<jupyter_output><empty_output><jupyter_text>Using `cf_units`In many routines, checking and converting betweeen units is complicated. But in Python, we have a great tool `cf_units` that can do this quickly and easily as long as the data is CF-compliant.**What is cf_units?** Cf_units is a package that stores, combines, and compares physical units, allowing the user to perform unit conversion. You can read more about this package [here](https://scitools.org.uk/cf-units/docs/latest/unit.html).Let's import the `cf_units` library. The first functionality we will use is `cf_units.Unit()`, where you can pass in a unit of measure either as a string or by pointing to an attribute containing units. Cf_units checks if this unit is supported and converts it to the class `Units`.First, we will look at the units for a single point along the levels dimension.<jupyter_code>import cf_units as cf

level_point = ds['lev'][0]
level_point
orig_units = cf.Unit(level_point.attrs['units'])
orig_units<jupyter_output><empty_output><jupyter_text>Then we will use `cf_units.Unit.convert` to convert from our original units to our target units.<jupyter_code>target_units = cf.Unit('km')
orig_units.convert(level_point, target_units)<jupyter_output><empty_output><jupyter_text>Task 1 - Perform unit conversion on a temperature value Use cf_units to convert the units of `thetao_point` into Kelvin (`degK`):<jupyter_code>thetao_point = ds['thetao'].isel(time=0, lev = 0, lat = 30, lon=30)
# Your code here
# %load solutions/solution_3_1.py<jupyter_output><empty_output><jupyter_text>Cf_units is nice because you can tell the code to convert your data to a specific unit without needing to explicitly know your current units. This allows more consistency in your workflows, so you can use the same code for temperature in C or in K. --------------  Using `apply_ufunc`You will notice that the `cf_units.Unit.convert` function caused us to lose the information contained in our Xarray DataArray. We will fix this by using `xarray.apply_ufunc` (u_func refers to "user function"). What is apply_ufunc?`apply_ufunc` is a tool from the Xarray package that maps functions. It allows you to apply a function to every element of a DataArray while maintaining Xarray's attribute formatting and functionality. You can read more about `xarray.apply_ufunc` [here](http://xarray.pydata.org/en/stable/generated/xarray.apply_ufunc.html).In this example the arguments to `apply_ufunc` are the function (`orig_units.convert`), then the input arguments for that function (the DataArray and target units) and `output_dtypes` (where we specify the datatype of the output as identical to the input datatype).<jupyter_code>orig_units = cf.Unit(ds['lev'].attrs['units'])
target_units = cf.Unit('km')

level_bounds_in_km = xr.apply_ufunc(orig_units.convert, ds.lev_bnds, target_units,
                                    output_dtypes=[ds.lev_bnds.dtype])
level_bounds_in_km<jupyter_output><empty_output><jupyter_text>Task 2 -  Write a function for unit conversionThis is only took us three lines of code to write, but you may want to check, convert, or assert desired units for every variable in a dataset. Your code will be much easier to read if this process is inside a function which you call up in one line. So let's write a function for unit conversion in the code cell block below:<jupyter_code># Your code here<jupyter_output><empty_output><jupyter_text>Here is my function:<jupyter_code># %load solutions/solution_3_2.py<jupyter_output><empty_output><jupyter_text>I used inputs of the dataset, the variable in that dataset, the variable bounds (because often the unit attribute is associated with the variable but you want to adjust values of the variable bounds too), and the target units. You may have used different inputs.  Task 3 -  Call up your unit conversion functionNow use your function to assert your level bounds (`lev_bnds`) and temperature (`thetao`) DataSet variables units to that of meters ('m') and Kelvins ('degK') in the code cell block below:<jupyter_code># Your code here<jupyter_output><empty_output><jupyter_text>For my function, this looks as follows:<jupyter_code># %load solutions/solution_3_3.py<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.0889922480620155, 'fraction_numerical': 0.012248062015503876, 'mean_word_length': 5.425298804780876, 'pattern_counts': {'":': 0, '<': 46, '<?xml version=': 0, '>': 45, 'https://': 1, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 1, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '11', 'dir': 'jupyter-structured-clean-dedup', 'id': '48753', 'license': 'CC-BY-4.0', 'n_tokens_mistral': 1966, 'n_tokens_neox': 1887, 'n_words': 711, 'path': 'notebooks/workflows/ocean_heat_content/03_units.ipynb', 'repo_name': 'khallock/ncar-python-tutorial', 'size': '6450'}