Repository: modin-project/modin
Pull request: TEST-#5420: port asv benchmarks for Repr, MaskBool, isNull, dropNa and equals functions
<!--
Thank you for your contribution!
Please review the contributing docs: https://modin.readthedocs.io/en/latest/development/contributing.html
if you have questions about contributing.
-->

## What do these changes do?

<!-- Please give a short brief about these changes. -->

Added asv benchmarks for Repr, MaskBool, isNull, dropNa and equals functions
Source PR https://github.com/modin-project/modin/pull/5036

- [x] first commit message and PR title follow format outlined [here](https://modin.readthedocs.io/en/latest/development/contributing.html#commit-message-formatting)
  > **_NOTE:_**  If you edit the PR title to match this format, you need to add another commit (even if it's empty) or amend your last commit for the CI job that checks the PR title to pick up the new PR title.
- [x] passes `flake8 modin/ asv_bench/benchmarks scripts/doc_checker.py`
- [x] passes `black --check modin/ asv_bench/benchmarks scripts/doc_checker.py`
- [x] signed commit with `git commit -s` <!-- you can amend your commit with a signature via `git commit -amend -s` -->
- [x] Resolves #5420  <!-- issue must be created for each patch -->
- [x] tests added and passing
- [x] module layout described at `docs/development/architecture.rst` is up-to-date <!-- if you have added, renamed or removed files or directories please update the documentation accordingly -->


Problem statement:
[ASV] port Repr, MaskBool, isNull, dropNa and equals benchmarks from pandas github
Have to add ASV benchmarks  which does the following actions

Gets string representation of dataframe
Applies a mask on a dataframe
Detect missing values 
remove missing values
check equality of dataframes

Hints from issue discussion:

Commit message: fixed a flake issue

Signed-off-by: arunjose696 <arunjose696@gmail.com>

Patch to review:
diff --git a/asv_bench/benchmarks/benchmarks.py b/asv_bench/benchmarks/benchmarks.py
index d4777d2393e..46ea5a7c402 100644
--- a/asv_bench/benchmarks/benchmarks.py
+++ b/asv_bench/benchmarks/benchmarks.py
@@ -1184,4 +1184,96 @@ def time_series_indices(self, shape):
         self.series.groupby(self.series).indices
 
 
+class TimeRepr:
+
+    params = [get_benchmark_shapes("TimeRepr")]
+    param_names = ["shape"]
+
+    def setup(self, shape):
+        self.df = IMPL.DataFrame(np.random.randn(*shape))
+        execute(self.df)
+
+    # returns a  string thus not calling execute
+    def time_repr(self, shape):
+        repr(self.df)
+
+
+class TimeMaskBool:
+
+    params = [get_benchmark_shapes("TimeMaskBool")]
+    param_names = ["shape"]
+
+    def setup(self, shape):
+        df = IMPL.DataFrame(np.random.randn(*shape))
+        df = df.where(df > 0)
+        self.bools = df > 0
+        self.mask = IMPL.isnull(df)
+        execute(self.bools), execute(self.mask)
+
+    def time_frame_mask_bools(self, shape):
+        execute(self.bools.mask(self.mask))
+
+    def time_frame_mask_floats(self, shape):
+        execute(self.bools.astype(float).mask(self.mask))
+
+
+class TimeIsnull:
+
+    params = [get_benchmark_shapes("TimeIsnull")]
+    param_names = ["shape"]
+
+    def setup(self, shape):
+        self.df_no_null = IMPL.DataFrame(np.random.randn(*shape))
+        sample = np.array([np.nan, 1.0])
+        data = np.random.choice(sample, (shape[0], shape[1]))
+        self.df = IMPL.DataFrame(data)
+        execute(self.df), execute(self.df_no_null)
+
+    def time_isnull_floats_no_null(self, shape):
+        execute(IMPL.isnull(self.df_no_null))
+
+    def time_isnull(self, shape):
+        execute(IMPL.isnull(self.df))
+
+
+class TimeDropna:
+
+    params = (["all", "any"], [0, 1], get_benchmark_shapes("TimeDropna"))
+    param_names = ["how", "axis", "shape"]
+
+    def setup(self, how, axis, shape):
+        row, col = shape
+        self.df = IMPL.DataFrame(np.random.randn(row, col))
+        self.df.iloc[row // 20 : row // 10, col // 3 : col // 2] = np.nan
+        self.df_mixed = self.df.copy()
+        self.df_mixed["foo"] = "bar"
+        execute(self.df), execute(self.df_mixed)
+
+    def time_dropna(self, how, axis, shape):
+        execute(self.df.dropna(how=how, axis=axis))
+
+    def time_dropna_axis_mixed_dtypes(self, how, axis, shape):
+        execute(self.df_mixed.dropna(how=how, axis=axis))
+
+
+class TimeEquals:
+
+    params = [get_benchmark_shapes("TimeEquals")]
+    param_names = ["shape"]
+
+    def setup(self, shape):
+        self.df = IMPL.DataFrame(np.random.randn(*shape))
+        self.df_nan = self.df.copy()
+        self.df_nan.iloc[-1, -1] = np.nan
+        execute(self.df), execute(self.df_nan)
+
+    # returns a  boolean thus not calling execute
+    def time_frame_float_equal(self, shape):
+        self.df.equals(self.df)
+
+    # returns a  boolean thus not calling execute
+    def time_frame_float_unequal(self, shape):
+        self.df.equals(self.df_nan)
+
+
 from .utils import setup  # noqa: E402, F401
diff --git a/asv_bench/benchmarks/utils/data_shapes.py b/asv_bench/benchmarks/utils/data_shapes.py
index 0159d756f09..098cc02d489 100644
--- a/asv_bench/benchmarks/utils/data_shapes.py
+++ b/asv_bench/benchmarks/utils/data_shapes.py
@@ -103,6 +103,11 @@
             "TimeDropDuplicatesDataframe",
             "TimeStack",
             "TimeUnstack",
+            "TimeRepr",
+            "TimeMaskBool",
+            "TimeIsnull",
+            "TimeDropna",
+            "TimeEquals",
             # IO benchmarks
             "TimeReadCsvSkiprows",
             "TimeReadCsvTrueFalseValues",


Code review comments:
diff_hunk: @@ -1184,4 +1184,96 @@ def time_series_indices(self, shape):
         self.series.groupby(self.series).indices
 
 
+class TimeRepr:
+
+    params = [get_benchmark_shapes("TimeRepr")]
+    param_names = ["shape"]
+
+    def setup(self, shape):
+        self.df = IMPL.DataFrame(np.random.randn(*shape))
+        execute(self.df)
+
+    # returns a  string thus not calling execute
+    def time_repr(self, shape):
+        repr(self.df)
+
+
+class TimeMaskBool:
+
+    params = [get_benchmark_shapes("TimeMaskBool")]
+    param_names = ["shape"]
+
+    def setup(self, shape):
+        df = IMPL.DataFrame(np.random.randn(*shape))
+        df = df.where(df > 0)
+        self.bools = df > 0
+        self.mask = IMPL.isnull(df)
+        execute(self.bools), execute(self.mask)
+
+    def time_frame_mask_bools(self, shape):
+        execute(self.bools.mask(self.mask))
+
+    def time_frame_mask_floats(self, shape):
+        execute(self.bools.astype(float).mask(self.mask))
+
+
+class TimeIsnull:
+
+    params = [get_benchmark_shapes("TimeIsnull")]
+    param_names = ["shape"]
+
+    def setup(self, shape):
+        self.df_no_null = IMPL.DataFrame(np.random.randn(*shape))
+        sample = np.array([np.nan, 1.0])
+        data = np.random.choice(sample, (shape[0], shape[1]))
+        self.df = IMPL.DataFrame(data)
+        execute(self.df), execute(self.df_no_null)
+
+    def time_isnull_floats_no_null(self, shape):
+        execute(IMPL.isnull(self.df_no_null))
path: asv_bench/benchmarks/benchmarks.py
text: @user1:
Let's drop "no_null" case. No big difference for Modin.

```suggestion
        sample = np.array([np.nan, 1.0])
        data = np.random.choice(sample, (shape[0], shape[1]))
        self.df = IMPL.DataFrame(data)
        execute(self.df)
```

diff_hunk: @@ -1184,4 +1184,96 @@ def time_series_indices(self, shape):
         self.series.groupby(self.series).indices
 
 
+class TimeRepr:
+
+    params = [get_benchmark_shapes("TimeRepr")]
+    param_names = ["shape"]
+
+    def setup(self, shape):
+        self.df = IMPL.DataFrame(np.random.randn(*shape))
+        execute(self.df)
+
+    # returns a  string thus not calling execute
+    def time_repr(self, shape):
+        repr(self.df)
+
+
+class TimeMaskBool:
+
+    params = [get_benchmark_shapes("TimeMaskBool")]
+    param_names = ["shape"]
+
+    def setup(self, shape):
+        df = IMPL.DataFrame(np.random.randn(*shape))
+        df = df.where(df > 0)
+        self.bools = df > 0
+        self.mask = IMPL.isnull(df)
+        execute(self.bools), execute(self.mask)
+
+    def time_frame_mask_bools(self, shape):
+        execute(self.bools.mask(self.mask))
+
+    def time_frame_mask_floats(self, shape):
+        execute(self.bools.astype(float).mask(self.mask))
+
+
+class TimeIsnull:
+
+    params = [get_benchmark_shapes("TimeIsnull")]
+    param_names = ["shape"]
+
+    def setup(self, shape):
+        self.df_no_null = IMPL.DataFrame(np.random.randn(*shape))
+        sample = np.array([np.nan, 1.0])
+        data = np.random.choice(sample, (shape[0], shape[1]))
+        self.df = IMPL.DataFrame(data)
+        execute(self.df), execute(self.df_no_null)
+
+    def time_isnull_floats_no_null(self, shape):
+        execute(IMPL.isnull(self.df_no_null))
+
+    def time_isnull(self, shape):
+        execute(IMPL.isnull(self.df))
+
+
+class TimeDropna:
+
+    params = (["all", "any"], [0, 1], get_benchmark_shapes("TimeDropna"))
+    param_names = ["how", "axis", "shape"]
+
+    def setup(self, how, axis, shape):
+        row, col = shape
+        self.df = IMPL.DataFrame(np.random.randn(row, col))
+        self.df.iloc[row // 20 : row // 10, col // 3 : col // 2] = np.nan
+        self.df_mixed = self.df.copy()
+        self.df_mixed["foo"] = "bar"
+        execute(self.df), execute(self.df_mixed)
+
+    def time_dropna(self, how, axis, shape):
+        execute(self.df.dropna(how=how, axis=axis))
+
+    def time_dropna_axis_mixed_dtypes(self, how, axis, shape):
+        execute(self.df_mixed.dropna(how=how, axis=axis))
+
+
+class TimeEquals:
+
+    params = [get_benchmark_shapes("TimeEquals")]
+    param_names = ["shape"]
+
+    def setup(self, shape):
+        self.df = IMPL.DataFrame(np.random.randn(*shape))
+        self.df_nan = self.df.copy()
+        self.df_nan.iloc[-1, -1] = np.nan
+        execute(self.df), execute(self.df_nan)
+
+    # returns a  boolean thus not calling execute
+    def time_frame_float_equal(self, shape):
+        self.df.equals(self.df)
+
+    # returns a  boolean thus not calling execute
+    def time_frame_float_unequal(self, shape):
+        self.df.equals(self.df_nan)
path: asv_bench/benchmarks/benchmarks.py
text: @user1:
Let's drop one case for Modin. Having another case is enough to track performance for `equals`.

```suggestion
    def setup(self, shape):
        self.df = IMPL.DataFrame(np.random.randn(*shape))
        self.df.iloc[-1, -1] = np.nan
        execute(self.df)

    # returns a boolean thus not calling execute
    def time_frame_float_unequal(self, shape):
        self.df.equals(self.df_nan)
```

diff_hunk: @@ -1184,4 +1184,96 @@ def time_series_indices(self, shape):
         self.series.groupby(self.series).indices
 
 
+class TimeRepr:
+
+    params = [get_benchmark_shapes("TimeRepr")]
+    param_names = ["shape"]
+
+    def setup(self, shape):
+        self.df = IMPL.DataFrame(np.random.randn(*shape))
+        execute(self.df)
+
+    # returns a  string thus not calling execute
+    def time_repr(self, shape):
+        repr(self.df)
+
+
+class TimeMaskBool:
+
+    params = [get_benchmark_shapes("TimeMaskBool")]
+    param_names = ["shape"]
+
+    def setup(self, shape):
+        df = IMPL.DataFrame(np.random.randn(*shape))
+        df = df.where(df > 0)
+        self.bools = df > 0
+        self.mask = IMPL.isnull(df)
+        execute(self.bools), execute(self.mask)
+
+    def time_frame_mask_bools(self, shape):
path: asv_bench/benchmarks/benchmarks.py
text: @user1:
Let's drop `time_frame_mask_bools` case.

@author:
done