From ef5f01ab703d41315ab8becb5a0b7f22000dbd9c Mon Sep 17 00:00:00 2001 From: Tim Mensinger Date: Sun, 3 Mar 2024 19:26:45 +0100 Subject: [PATCH] Call .map() if pandas version is 2.1.0 or greater --- src/estimagic/config.py | 19 +++++++++++--- src/estimagic/optimization/optimize_result.py | 4 +-- src/estimagic/utilities.py | 26 +++++++++++++++++++ .../visualization/estimation_table.py | 22 +++++++++------- 4 files changed, 55 insertions(+), 16 deletions(-) diff --git a/src/estimagic/config.py b/src/estimagic/config.py index dd0fcde1c..efa004724 100644 --- a/src/estimagic/config.py +++ b/src/estimagic/config.py @@ -1,4 +1,6 @@ from pathlib import Path +import pandas as pd +from packaging import version import plotly.express as px @@ -19,9 +21,9 @@ CRITERION_PENALTY_SLOPE = 0.1 CRITERION_PENALTY_CONSTANT = 100 -# ===================================================================================== +# ====================================================================================== # Check Available Packages -# ===================================================================================== +# ====================================================================================== try: from petsc4py import PETSc # noqa: F401 @@ -103,9 +105,18 @@ IS_NUMBA_INSTALLED = True -# ================================================================================= +# ====================================================================================== +# Check if pandas version is newer or equal to version 2.1.0 +# ====================================================================================== + +IS_PANDAS_VERSION_NEWER_OR_EQUAL_TO_2_1_0 = version.parse( + pd.__version__ +) >= version.parse("2.1.0") + + +# ====================================================================================== # Dashboard Defaults -# ================================================================================= +# ====================================================================================== Y_RANGE_PADDING = 0.05 Y_RANGE_PADDING_UNITS = "absolute" diff --git a/src/estimagic/optimization/optimize_result.py b/src/estimagic/optimization/optimize_result.py index e991f5ebb..7ff2c18c6 100644 --- a/src/estimagic/optimization/optimize_result.py +++ b/src/estimagic/optimization/optimize_result.py @@ -4,7 +4,7 @@ import numpy as np import pandas as pd -from estimagic.utilities import to_pickle +from estimagic.utilities import to_pickle, pandas_df_map @dataclass @@ -128,7 +128,7 @@ def _format_convergence_report(report, algorithm): report = pd.DataFrame.from_dict(report) columns = ["one_step", "five_steps"] - table = report[columns].applymap(_format_float).astype(str) + table = pandas_df_map(report[columns], _format_float).astype(str) for col in "one_step", "five_steps": table[col] = table[col] + _create_stars(report[col]) diff --git a/src/estimagic/utilities.py b/src/estimagic/utilities.py index 4d4d119b3..81f45a0be 100644 --- a/src/estimagic/utilities.py +++ b/src/estimagic/utilities.py @@ -6,6 +6,7 @@ import numpy as np import pandas as pd from scipy.linalg import ldl, qr +from estimagic.config import IS_PANDAS_VERSION_NEWER_OR_EQUAL_TO_2_1_0 with warnings.catch_warnings(): warnings.simplefilter("ignore", category=UserWarning) @@ -321,3 +322,28 @@ def get_rng(seed): else: raise TypeError("seed type must be in {None, int, numpy.random.Generator}.") return rng + + +def pandas_df_map(df, func, na_action=None, **kwargs): + """Apply a function to a Dataframe elementwise. + + pandas has depricated the .applymap() function with version 2.1.0. This function + calls either .map() (if pandas version is greater or equal to 2.1.0) or .applymap() + (if pandas version is smaller than 2.1.0). + + Args: + df (pd.DataFrame): A pandas DataFrame. + func (callable): Python function, returns a single value from a single value. + na_action (str): If 'ignore', propagate NaN values, without passing them to + func. If None, pass NaN values to func. Default is None. + **kwargs: Additional keyword arguments to pass as keywords arguments to func. + + Returns: + pd.DataFrame: Transformed DataFrame. + + """ + if IS_PANDAS_VERSION_NEWER_OR_EQUAL_TO_2_1_0: + out = df.map(func, na_action=na_action, **kwargs) + else: + out = df.applymap(func, na_action=na_action, **kwargs) + return out diff --git a/src/estimagic/visualization/estimation_table.py b/src/estimagic/visualization/estimation_table.py index 48d611c7e..fb5297f87 100644 --- a/src/estimagic/visualization/estimation_table.py +++ b/src/estimagic/visualization/estimation_table.py @@ -3,6 +3,7 @@ from functools import partial from pathlib import Path from warnings import warn +from estimagic.utilities import pandas_df_map import numpy as np import pandas as pd @@ -305,7 +306,7 @@ def render_latex( ci_in_body = False if ci_in_body: - body.loc[("",)] = body.loc[("",)].applymap("{{{}}}".format).values + body.loc[("",)] = pandas_df_map(body.loc[("",)], "{{{}}}".format).values if body.columns.nlevels > 1: column_groups = body.columns.get_level_values(0) else: @@ -1383,22 +1384,23 @@ def _apply_number_format(df_raw, number_format, format_integers): if isinstance(processed_format, (list, tuple)): df_formatted = df_raw.copy(deep=True).astype("float") for formatter in processed_format[:-1]: - df_formatted = df_formatted.applymap(formatter.format).astype("float") - df_formatted = df_formatted.astype("float").applymap( - processed_format[-1].format + df_formatted = pandas_df_map(df_formatted, formatter.format).astype("float") + df_formatted = pandas_df_map( + df_formatted.astype("float"), processed_format[-1].format ) elif isinstance(processed_format, str): - df_formatted = df_raw.astype("str").applymap( - partial(_format_non_scientific_numbers, format_string=processed_format) + df_formatted = pandas_df_map( + df_raw.astype("str"), + partial(_format_non_scientific_numbers, format_string=processed_format), ) elif callable(processed_format): - df_formatted = df_raw.applymap(processed_format) + df_formatted = pandas_df_map(df_raw, processed_format) # Don't format integers: set to original value if not format_integers: - integer_locs = df_raw.applymap(_is_integer) - df_formatted[integer_locs] = ( - df_raw[integer_locs].astype(float).applymap("{:.0f}".format) + integer_locs = pandas_df_map(df_raw, _is_integer) + df_formatted[integer_locs] = pandas_df_map( + df_raw[integer_locs].astype(float), "{:.0f}".format ) return df_formatted