# Buggy code
def broadcast_apply(cls, axis, apply_func, left, right):
    map_func = ray.put(apply_func)
    right_parts = np.squeeze(right)
    if len(right_parts.shape) == 0:
        right_parts = np.array([right_parts.item()])
    assert len(right_parts.shape) == 1, (
        "Invalid broadcast partitions shape {}\n{}".format(
            right_parts.shape, [[i.get() for i in j] for j in right_parts]
        )
    )
    return np.array(
        [
            [
                PandasOnRayFramePartition(
                    func.remote(
                        part.oid,
                        right_parts[col_idx].oid if axis else right_parts[row_idx].oid,
                        map_func,
                        part.call_queue,
                        right_parts[col_idx].call_queue
                        if axis
                        else right_parts[row_idx].call_queue,
                    )
                )
                for col_idx, part in enumerate(left[row_idx])
            ]
            for row_idx in range(len(left))
        ]
    )


# Traceback (AttributeError)
import modin.pandas as pd
import glob

files = glob.glob('/path/to/files/*.parquet')
df = pd.concat([pd.read_parquet(file) for file in files])
df.reset_index(inplace=True)
df.drop(columns=['index', 'DATE', 'STATUS', 'ID', 'ID2],inplace=True)

df.groupby(['REPORTING_DATE', 'ENTITY', 'CURRENCY']).sum()

UserWarning: Multi-column groupby is a new feature. Please report any bugs/issues to bug_reports@modin.org.
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-15-081b1819b7d6> in <module>
----> 1 df.groupby(['DATA_SALDA', 'BANK', 'WALUTA_KON']).sum()

~/eksperyment/lib64/python3.6/site-packages/modin/pandas/groupby.py in sum(self, **kwargs)
323
324     def sum(self, **kwargs):
--> 325         return self._groupby_reduce(lambda df: df.sum(**kwargs), None)
326
327     def describe(self, **kwargs):

~/eksperyment/lib64/python3.6/site-packages/modin/pandas/groupby.py in _groupby_reduce(self, map_func, reduce_func, drop, numeric_only, **kwargs)
479                 reduce_args=kwargs,
480                 numeric_only=numeric_only,
--> 481                 drop=self._drop,
482             )
483         )

~/eksperyment/lib64/python3.6/site-packages/modin/backends/pandas/query_compiler.py in groupby_reduce(self, by, axis, groupby_args, map_func, map_args, reduce_func, reduce_args, numeric_only, drop)
1236             _reduce,
1237             new_columns=new_columns,
-> 1238             new_index=new_index,
1239         )
1240         result = self.__constructor__(new_modin_frame)

~/eksperyment/lib64/python3.6/site-packages/modin/engines/base/frame/data.py in groupby_reduce(self, axis, by, map_func, reduce_func, new_index, new_columns)
1148         """
1149         new_partitions = self._frame_mgr_cls.groupby_reduce(
-> 1150             axis, self._partitions, by._partitions, map_func, reduce_func
1151         )
1152         if new_columns is None:

~/eksperyment/lib64/python3.6/site-packages/modin/engines/base/frame/partition_manager.py in groupby_reduce(cls, axis, partitions, by, map_func, reduce_func)
66         if len(by_parts.shape) == 0:
67             by_parts = np.array([by_parts.item()])
---> 68         [obj.drain_call_queue() for obj in by_parts]
69         new_partitions = np.array(
70             [

~/eksperyment/lib64/python3.6/site-packages/modin/engines/base/frame/partition_manager.py in <listcomp>(.0)
66         if len(by_parts.shape) == 0:
67             by_parts = np.array([by_parts.item()])
---> 68         [obj.drain_call_queue() for obj in by_parts]
69         new_partitions = np.array(
70             [

AttributeError: 'numpy.ndarray' object has no attribute 'drain_call_queue'

# Fixed code
def broadcast_apply(cls, axis, apply_func, left, right, other_name="r"):
    def mapper(df, others):
        other = pandas.concat(others, axis=axis ^ 1)
        return apply_func(df, **{other_name: other})

    mapper = ray.put(mapper)
    new_partitions = np.array(
        [
            [
                PandasOnRayFramePartition(
                    func.remote(
                        part.oid,
                        mapper,
                        part.call_queue,
                        [obj[col_idx].call_queue for obj in right]
                        if axis
                        else [obj.call_queue for obj in right[row_idx]],
                        *(
                            [obj[col_idx].oid for obj in right]
                            if axis
                            else [obj.oid for obj in right[row_idx]]
                        ),
                    )
                )
                for col_idx, part in enumerate(left[row_idx])
            ]
            for row_idx in range(len(left))
        ]
    )

    return new_partitions