Skip to content

REF: prepare (upcast) scalar before dispatching to arithmetic array ops #40479

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged

Conversation

jorisvandenbossche
Copy link
Member

This moves the potential upcasting of python/numpy scalars to pandas scalars out of the actual array op (arithmetic_op), but moves it up to the Series / DataFrame level before calling the array op. This avoids calling it repeatedly on the same scalar for each column.

@jorisvandenbossche jorisvandenbossche added Refactor Internal refactoring of code Performance Memory or execution speed performance Numeric Operations Arithmetic, Comparison, and Logical operations labels Mar 17, 2021
@jbrockmendel
Copy link
Member

How costly is this? I get that its a valid optimization, but it breaks the abstraction of the self-contained op.

@github-actions
Copy link
Contributor

This pull request is stale because it has been open for thirty days with no activity. Please update or respond to this comment if you're still interested in working on this.

@jorisvandenbossche
Copy link
Member Author

jorisvandenbossche commented Apr 22, 2021

How costly is this?

In the same FrameWithFrameWide benchmark (as mentioned in #39820 (comment)), calls to _maybe_upcast_for_op currently take around 8% on master. But I expect this number to become (relatively) bigger with other optimizations merged (like #39820). For reference, the actual add operator only takes around 18% in this benchmark, with everything else being overhead that can be partly optimized away.

I get that its a valid optimization, but it breaks the abstraction of the self-contained op.

We already do other preparation steps before the actual array_op as well (extract_array, extracting zerodim, checking length, wrapping datetimelike, etc). So I don't think the abstraction holds right now, and there is certainly an opportunity to organize this better. For example, we can ensure to have a clear set of steps (factored out as consistent helper functions) that need to be taken in the different code paths where array_op gets called (eg both in series op as frame op)

@@ -422,7 +422,7 @@ def get_array_op(op):
raise NotImplementedError(op_name)


def _maybe_upcast_for_op(obj, shape: Shape):
def prepare_scalar_for_op(obj, shape: Shape):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think to be pandas-idiomatic might need to keep a "maybe"?

@jbrockmendel
Copy link
Member

We already do other preparation steps before the actual array_op

Fair enough.

Generally LGTM. Should this wait until after the ensure_wrapped_if_datetimelike PR so that can be folded into this helper?

@@ -428,6 +429,7 @@ def f(self, other, axis=default_axis, level=None, fill_value=None):

axis = self._get_axis_number(axis) if axis is not None else 1

other = maybe_prepare_scalar_for_op(other, (self.shape[axis],))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why self.shape[axis] instead of self.shape?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because self is a dataframe, and we want a 1D shape (rows or columns depending on the axis)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

passing shape only matters when we have a scalar, in which case broadcasting to self.shape is simpler

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, changed to self.shape

@jreback jreback added this to the 1.3 milestone Apr 26, 2021
@jreback
Copy link
Contributor

jreback commented Apr 26, 2021

looks fine. this helps with perf?

@jorisvandenbossche
Copy link
Member Author

this helps with perf?

Yes, see #40479 (comment)

@jreback jreback merged commit ce1843f into pandas-dev:master Apr 28, 2021
@jreback
Copy link
Contributor

jreback commented Apr 28, 2021

thanks @jorisvandenbossche

@jorisvandenbossche jorisvandenbossche deleted the ops-refactor-prepare-scalar branch April 28, 2021 14:50
yeshsurya pushed a commit to yeshsurya/pandas that referenced this pull request May 6, 2021
JulianWgs pushed a commit to JulianWgs/pandas that referenced this pull request Jul 3, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Numeric Operations Arithmetic, Comparison, and Logical operations Performance Memory or execution speed performance Refactor Internal refactoring of code
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants