Example #1
0
def _convert_currency(
    from_currency: str = None,
    to_currency: str = None,
    historical_date: Optional[date] = None,
) -> float:
    """
    Currency conversion for Pandas DataFrame column.

    Helper function for `convert_currency` method.

    The API used is: https://exchangeratesapi.io/
    """

    url = "https://api.exchangeratesapi.io"

    if historical_date:
        check("historical_date", historical_date, [datetime, date])
        if isinstance(historical_date, datetime):
            if historical_date < datetime(1999, 1, 4):
                raise ValueError(
                    "historical_date:datetime must be later than 1999-01-04!"
                )
            string_date = str(historical_date)[:10]
        else:
            if historical_date < date(1999, 1, 4):
                raise ValueError(
                    "historical_date:date must be later than 1999-01-04!"
                )
            string_date = str(historical_date)
        url = url + "/%s" % string_date
    else:
        url = url + "/latest"

    _check_currency(from_currency)
    _check_currency(to_currency)

    payload = {"base": from_currency, "symbols": to_currency}

    result = requests.get(url, params=payload)

    if result.status_code != 200:
        raise ConnectionError(
            "Exchange Rate API failed to receive a 200 "
            "response from the server. "
            "Please try again later."
        )

    currency_dict = json.loads(result.text)
    rate = currency_dict["rates"][to_currency]
    return rate
Example #2
0
def _inflate_currency(
    country: str = None, currency_year: int = None, to_year: int = None
) -> float:
    """
    Currency inflation for Pandas DataFrame column.
    Helper function for `inflate_currency` method.
    The API used is the World Bank Indicator API:
    https://datahelpdesk.worldbank.org/knowledgebase/articles/889392-about-the-indicators-api-documentation
    """

    # Check all inputs are correct data type
    check("country", country, [str])
    check("currency_year", currency_year, [int])
    check("to_year", to_year, [int])

    # Get WB country abbreviation
    _check_wb_country(country)
    if country in wb_country_dict.keys():
        country = wb_country_dict[country]
    else:
        # `country` is already a correct abbreviation; do nothing
        pass

    _check_wb_years(currency_year)
    _check_wb_years(to_year)

    url = (
        "https://api.worldbank.org/v2/country/"
        + country
        + "/indicator/FP.CPI.TOTL?date="
        + str(min(currency_year, to_year))
        + ":"
        + str(max(currency_year, to_year))
        + "&format=json"
    )

    result = requests.get(url)

    if result.status_code != 200:
        raise ConnectionError(
            "WB Indicator API failed to receive a 200 "
            "response from the server. "
            "Please try again later."
        )

    # The API returns a list of two items;
    # the second item in the list is what we want
    inflation_dict = json.loads(result.text)[1]

    # Error checking
    if inflation_dict is None:
        raise ValueError(
            "The WB Indicator API returned nothing. "
            "This likely means the currency_year and "
            "to_year are outside of the year range for "
            "which the WB has inflation data for the "
            "specified country."
        )

    # Create new dict with only the year and inflation values
    inflation_dict_ready = {
        int(inflation_dict[i]["date"]): float(inflation_dict[i]["value"])
        for i in range(len(inflation_dict))
        if inflation_dict[i]["value"] is not None
    }

    # Error catching
    if currency_year not in inflation_dict_ready.keys():
        raise ValueError(
            f"The WB Indicator API does not have inflation "
            f"data for {currency_year} for {country}."
        )
    if to_year not in inflation_dict_ready.keys():
        raise ValueError(
            f"The WB Indicator API does not have inflation "
            f"data for {to_year} for {country}."
        )

    inflator = (
        inflation_dict_ready[to_year] / inflation_dict_ready[currency_year]
    )
    return inflator
Example #3
0
def convert_units(
    df: pd.DataFrame,
    column_name: str = None,
    existing_units: str = None,
    to_units: str = None,
    dest_column_name: str = None,
) -> pd.DataFrame:
    """
    Converts a column of numeric values from one unit to another.

    Functional usage example:

    .. code-block:: python

        import pandas as pd
        import janitor.engineering

        df = pd.DataFrame(...)

        df = janitor.engineering.convert_units(
            df=df,
            column_name='temp_F',
            existing_units='degF',
            to_units='degC',
            dest_column_name='temp_C'
        )

    Method chaining usage example:

    .. code-block:: python

        import pandas as pd
        import janitor.engineering

        df = pd.DataFrame(...)

        df = df.convert_units(
            column_name='temp_F',
            existing_units='degF',
            to_units='degC',
            dest_column_name='temp_C'
        )

    Unit conversion can only take place if the existing_units and
    to_units are of the same type (e.g., temperature or pressure).
    The provided unit types can be any unit name or alternate name provided
    in the unyt package's Listing of Units table:
    https://unyt.readthedocs.io/en/stable/unit_listing.html#unit-listing.

    Volume units are not provided natively in unyt.  However, exponents are
    supported, and therefore some volume units can be converted.  For example,
    a volume in cubic centimeters can be converted to cubic meters using
    existing_units='cm**3' and to_units='m**3'.

    This method mutates the original DataFrame.

    :param df: A pandas dataframe.
    :param column_name: Name of the column containing numeric
        values that are to be converted from one set of units to another.
    :param existing_units: The unit type to convert from.
    :param to_units: The unit type to convert to.
    :param dest_column_name: The name of the new column containing the
        converted values that will be created.

    :returns: A pandas DataFrame with a new column of unit-converted values.
    """

    # Check all inputs are correct data type
    check("column_name", column_name, [str])
    check("existing_units", existing_units, [str])
    check("to_units", to_units, [str])
    check("dest_column_name", dest_column_name, [str])

    # Check that column_name is a numeric column
    if not np.issubdtype(df[column_name].dtype, np.number):
        raise TypeError(f"{column_name} must be a numeric column.")

    original_vals = df[column_name].values * unyt.Unit(existing_units)
    converted_vals = original_vals.to(to_units)
    df[dest_column_name] = np.array(converted_vals)

    return df