def normalize_units(
    df,
    unitsmap,
    targetunit,
    paramcol="parameter",
    rescol="res",
    unitcol="units",
    napolicy="ignore",
):
    """
    Normalize units of measure in a dataframe.

    Parameters
    ----------
    df : pandas.DataFrame
        Dataframe contained results and units of measure data.
    unitsmap : dictionary
        Dictionary where keys are the units present in the df and
        values are the conversion factors required to standardize
        results to a common unit.
    targetunit : string or dict
        The desired final units of measure. Must be present in
        ``unitsmap``. If a string, all rows in df will be
        assigned ``targetunit``. If a dictionary, the units will be
        mapped from the dictionary using the values of ``paramcol`` as
        keys.
    paramcol, rescol, unitcol : string, optional
        Labels for the parameter, results, and units columns in the
        df.
    napolicy : string, optional
        Determines how mull/missing values are stored. By default,
        this is set to "ignore". Use "raise" to throw a ``ValueError``
        if when unit conversion or target units data cannot be found.

    Returns
    -------
    normalized : pandas.DataFrame
        Dataframe with normalized units of measure.

    """

    # determine the preferred units in the wqdata
    target = df[paramcol].map(targetunit)

    # factors to normialize to standard units
    normalization = df[unitcol].map(unitsmap)

    # factor to convert to preferred units
    conversion = target.map(unitsmap)

    if napolicy == "raise":
        msg = ""
        if target.isnull().any():
            nulls = df[target.isnull()][paramcol].unique()
            msg += "Some target units could not be mapped to the {} column ({})\n".format(
                paramcol, nulls
            )

        if normalization.isnull().any():
            nulls = df[normalization.isnull()][unitcol].unique()
            msg += "Some normalization factors could not be mapped to the {} column ({})\n".format(
                unitcol, nulls
            )

        if conversion.isnull().any():
            nulls = target[conversion.isnull()]
            msg += "Some conversion factors could not be mapped to the target units ({})".format(
                nulls
            )

        if len(msg) > 0:
            raise ValueError(msg)

    # convert results
    normalized = df.assign(
        **{rescol: df[rescol] * normalization / conversion, unitcol: target}
    )

    return normalized