# Buggy code
def apply(self, func, axis=0, raw=False, result_type=None, args=(), **kwds):
    axis = self._get_axis_number(axis)
    query_compiler = super(DataFrame, self).apply(
        func, axis=axis, raw=raw, result_type=result_type, args=args, **kwds
    )
    if not isinstance(query_compiler, type(self._query_compiler)):
        return query_compiler
    # This is the simplest way to determine the return type, but there are checks
    # in pandas that verify that some results are created. This is a challenge for
    # empty DataFrames, but fortunately they only happen when the `func` type is
    # a list or a dictionary, which means that the return type won't change from
    # type(self), so we catch that error and use `self.__name__` for the return
    # type.
    try:
        if axis == 0:
            init_kwargs = {"index": self.index}
        else:
            init_kwargs = {"columns": self.columns}
        return_type = type(
            getattr(pandas, self.__name__)(**init_kwargs).apply(
                func,
                axis=axis,
                raw=raw,
                result_type=result_type,
            )
        ).__name__
    except Exception:
        return_type = self.__name__
    if return_type not in ["DataFrame", "Series"]:
        return query_compiler.to_pandas().squeeze()
    else:
        result = getattr(sys.modules[self.__module__], return_type)(
            query_compiler=query_compiler
        )
        if hasattr(result, "name"):
            if axis == 0 and result.name == self.index[0]:
                result.name = None
            elif axis == 1 and result.name == self.columns[0]:
                result.name = None
        return result


# Traceback (AttributeError)
2020-02-27 10:22:13,644 INFO resource_spec.py:216 -- Starting Ray with 200.0 GiB memory available for workers and up to 200.0 GiB for objects. You can adjust these settings with ray.init(memory=<bytes>, object_store_memory=<bytes>).
2020-02-27 10:22:13,899 WARNING services.py:1365 -- WARNING: object_store_memory is not verified when plasma_directory is set.
UserWarning: Distributing <class 'list'> object. This may take some time.
name  max_speed  health
1    one          1      10
2    two          4      20
3  three          7      30
Traceback (most recent call last):
File "drop_duplicates_test.py", line 13, in <module>
df1 = df.drop_duplicates("name")
File "/nfs/site/proj/scripting_tools/gashiman/modin/modin/pandas/dataframe.py", line 208, in drop_duplicates
subset=subset, keep=keep, inplace=inplace
File "/nfs/site/proj/scripting_tools/gashiman/modin/modin/pandas/base.py", line 1120, in drop_duplicates
duplicates = self.duplicated(keep=keep, subset=kwargs.get("subset"))
File "/nfs/site/proj/scripting_tools/gashiman/modin/modin/pandas/dataframe.py", line 242, in duplicated
if len(df.columns) > 1:
File "/nfs/site/proj/scripting_tools/gashiman/modin/modin/pandas/series.py", line 227, in __getattr__
raise e
File "/nfs/site/proj/scripting_tools/gashiman/modin/modin/pandas/series.py", line 223, in __getattr__
return object.__getattribute__(self, key)
AttributeError: 'Series' object has no attribute 'columns'

# Fixed code
def apply(self, func, axis=0, raw=False, result_type=None, args=(), **kwds):
    axis = self._get_axis_number(axis)
    query_compiler = super(DataFrame, self).apply(
        func, axis=axis, raw=raw, result_type=result_type, args=args, **kwds
    )
    if not isinstance(query_compiler, type(self._query_compiler)):
        return query_compiler
    # This is the simplest way to determine the return type, but there are checks
    # in pandas that verify that some results are created. This is a challenge for
    # empty DataFrames, but fortunately they only happen when the `func` type is
    # a list or a dictionary, which means that the return type won't change from
    # type(self), so we catch that error and use `self.__name__` for the return
    # type.
    try:
        if axis == 0:
            init_kwargs = {"index": self.index}
        else:
            init_kwargs = {"columns": self.columns}
        return_type = type(
            getattr(pandas, self.__name__)(**init_kwargs).apply(
                func,
                axis=axis,
                raw=raw,
                result_type=result_type,
            )
        ).__name__
    except Exception:
        return_type = self.__name__
    if return_type not in ["DataFrame", "Series"]:
        return query_compiler.to_pandas().squeeze()
    else:
        result = getattr(sys.modules[self.__module__], return_type)(
            query_compiler=query_compiler
        )
        if isinstance(result, Series):
            if axis == 0 and result.name == self.index[0]:
                result.name = None
            elif axis == 1 and result.name == self.columns[0]:
                result.name = None
        return result