Repository: modin-project/modin

Issue:
CI: `test-fuzzydata` fails when `sample` uses a small `frac`
`test-fuzzydata` has been failing occasionally as can be seen here: https://github.com/modin-project/modin/actions/runs/3062497807/jobs/4943541816 

<details>
<summary>Stack trace</summary>

```python-traceback
/usr/share/miniconda3/envs/modin/lib/python3.8/site-packages/fuzzydata/core/generator.py:379: in generate_workflow
    raise e
/usr/share/miniconda3/envs/modin/lib/python3.8/site-packages/fuzzydata/core/generator.py:367: in generate_workflow
    wf.execute_current_operation(next_label)
/usr/share/miniconda3/envs/modin/lib/python3.8/site-packages/fuzzydata/core/workflow.py:197: in execute_current_operation
    raise e
/usr/share/miniconda3/envs/modin/lib/python3.8/site-packages/fuzzydata/core/workflow.py:168: in execute_current_operation
    new_artifact = self.current_operation.execute(new_label)
/usr/share/miniconda3/envs/modin/lib/python3.8/site-packages/fuzzydata/core/operation.py:172: in execute
    result = self.materialize(new_label)
/usr/share/miniconda3/envs/modin/lib/python3.8/site-packages/fuzzydata/clients/pandas.py:114: in materialize
    new_df = eval(self.code)
<string>:1: in <module>
    ???
modin/logging/logger_decorator.py:128: in run_and_log
    return obj(*args, **kwargs)
modin/_compat/pandas_api/latest/base.py:255: in sample
    return self._sample(
modin/logging/logger_decorator.py:128: in run_and_log
    return obj(*args, **kwargs)
modin/pandas/base.py:2451: in _sample
    return self._default_to_pandas(
modin/logging/logger_decorator.py:128: in run_and_log
    return obj(*args, **kwargs)
modin/pandas/base.py:431: in _default_to_pandas
    result = getattr(self._pandas_class, op)(pandas_obj, *args, **kwargs)
/usr/share/miniconda3/envs/modin/lib/python3.8/site-packages/pandas/core/generic.py:5438: in sample
    size = sample.process_sampling_size(n, frac, replace)
```

</details>


Discussion:
@suhailrehman can you tell what's causing the error?
I will have to rerun the workflow to look at it, but essentially fuzzydata randomly generated a bunch of operations and tried to sample a dataframe twice and failed to execute the operation because of an API mismatch (both `frac` and `n` were provided as arguments to sample), 

https://github.com/modin-project/modin/actions/runs/3062497807/jobs/4943541816#step:6:110 

Fuzzy testing yields errors like this because it's specifically randomizing and testing combinations of operations to find errors. This might be on the fuzzydata side (perhaps sampling the dataframe twice yielded 0 rows). Will update this thread when I get a chance. 

@mvashishtha @suhailrehman It looks like it may not be an issue with how we are using it. It might be an issue on the fuzzydata end. I see that in the pandas API for `sample` (https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.sample.html), `n` and `frac` cannot be used together so somehow they are both getting set when fuzzydata tries to materialize data here: https://github.com/suhailrehman/fuzzydata/blob/a16c75d54387224256aab20a4a32c97bccfa027c/fuzzydata/clients/pandas.py#L114 

However from what I'm seeing, there doesn't seem to be a place where `n=` is set internally with fuzzydata.
@mvashishtha @suhailrehman I spent some time digging into the fuzzydata code base and I don't think the issue is there. Rather, I think we are hitting a very, very specific edge case in how Modin handles `sample`. If you see here: https://github.com/modin-project/modin/blob/eca9a936846faa31b116a1e58b1114d90cfa44d8/modin/pandas/base.py#L2439, it's actually possible to end up getting `n` to be 0, so it'll end up executing the wrong code path that has `n` and `frac` set.
@pyrito These are precisely the kinds of errors I wanted this project to surface! Thanks for digging in!

I haven't been monitoring this modin integration heavily. But I have some free time this week, and I think I can make some improvements to help improve the testing/debugging experience):

1. I think the same workflow should run on both dask and ray (right now, it's generating two separate random workflows running on ray and dask, respectively)
2. Artifacts are not being written out on failure (significant pain to rerun and verify if the bug has been fixed)
3. fuzzydata should also run the workflow against pandas to measure performance regressions - maybe if the scale factor is significant enough to warrant it - the primary focus right now is correctness and not performance.




Resolving patch:
diff --git a/modin/pandas/base.py b/modin/pandas/base.py
--- a/modin/pandas/base.py
+++ b/modin/pandas/base.py
@@ -2448,10 +2448,11 @@ def _sample(
         if n == 0:
             # This returns an empty object, and since it is a weird edge case that
             # doesn't need to be distributed, we default to pandas for n=0.
+            # We don't need frac to be set to anything since n is already 0.
             return self._default_to_pandas(
                 "sample",
                 n=n,
-                frac=frac,
+                frac=None,
                 replace=replace,
                 weights=weights,
                 random_state=random_state,


Test patch:
diff --git a/modin/pandas/test/dataframe/test_indexing.py b/modin/pandas/test/dataframe/test_indexing.py
--- a/modin/pandas/test/dataframe/test_indexing.py
+++ b/modin/pandas/test/dataframe/test_indexing.py
@@ -1603,6 +1603,14 @@ def test_sample(data, axis):
     df_equals(modin_result, pandas_result)
 
 
+def test_empty_sample():
+    modin_df, pandas_df = create_test_dfs([1])
+    # issue #4983
+    # If we have a fraction of the dataset that results in n=0, we should
+    # make sure that we don't pass in both n and frac to sample internally.
+    eval_general(modin_df, pandas_df, lambda df: df.sample(frac=0.12))
+
+
 def test_select_dtypes():
     frame_data = {
         "test1": list("abc"),