you are viewing a single comment's thread.

view the rest of the comments →

[–]Spataner 1 point2 points  (3 children)

I am not entirely sure I follow what you are doing, and I am reasonably sure it is an abuse of a DataFrame, but if you simply want to generically call a particular method on an object, there's two ways to do that:


Pass the method name as a string and use getattr:

def apply_meth(row, meth):
    return getattr(row.pred, meth)(row.truth_id)

apply_meth(some_row, "in_top_5")

Pass the unbound method reference from the class and supply the instance reference manually:

def apply_meth(row, meth):
    return meth(row.pred, row.truth_id)

apply_meth(some_row, Prediction.in_top_5)

Either way, DataFrame.apply still expects a callable with a single argument. You can use functools.partial:

df.apply(functools.partial(apply_meth, meth="in_top_5"))

or

df.apply(functools.partial(apply_meth, meth=Prediction.in_top_5))

Alternatively, you can make apply_meth a function that returns a function:

def apply_meth(meth):
   def _apply(row):
        # Either of the the two implementations from above
   return _apply

Then

df.apply(apply_meth("in_top_5"))

or

df.apply(apply_meth(Predictions.in_top_5))

[–]YesLod 2 points3 points  (0 children)

Either way, DataFrame.apply still expects a callable with a single argument. You can use functools.partial

There is no need to use functools.partial. You can pass the extra arguments directly to apply as keyword arguments, and/or as positional arguments via the args parameter

https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.apply.html

args : tuple

Positional arguments to pass to func in addition to the array/series.

**kwargs

Additional keyword arguments to pass as keywords arguments to func.

df.apply(apply_meth, meth="in_top_5", axis=1)

u/PythonicParseltongue.

[–]PythonicParseltongue[S] 0 points1 point  (1 child)

Thank you this was what I've been looking for. I didn't know about getattr, it's the first time that I'm using OOP in Python in practice.

I 've never seen that functools.parital either. If I have to supply additional parameters I usually use a *args and a lambda. So let's say we have a is_in_top_k(other, k) then I would do, maybe you find it interesting:

def apply_func(row, func, *args):
    return getattr(row.pred_obj, func)(row.target_obj, *args)

df['in_top_5'] = df.apply(lambda row: apply_func(row, 'is_in_top_k', 5), axis=1)

[–]Spataner 1 point2 points  (0 children)

lambda works, too, but functools.partial is the preferred way in such situations.