class_docstr: Dodge overlaps and place objects side-by-side
  
  This is an enhanced version of
  [](`~plotnine.positions.position_dodge`) that can deal
  with rectangular overlaps that do not share a lower x border.
  
  Parameters
  ----------
  width :
      Dodging width, when different to the width of the
      individual elements. This is useful when you want
      to align narrow geoms with wider geoms
  preserve :
      Should dodging preserve the total width of all elements
      at a position, or the width of a single element?
  padding :
      Padding between elements at the same position.
      Elements are shrunk by this proportion to allow space
      between them.
  reverse :
      Reverse the default ordering of the groups. This is
      useful if you're rotating both the plot and legend.
class_docstr_tok: ["Dodge","overlaps","and","place","objects","side-by-side","This","is","an","enhanced","version","of","[","]","(","`","~plotnine.positions.position_dodge","`",")","that","can","deal","with","rectangular","overlaps","that","do","not","share","a","lower","x","border",".","Parameters","--","--","--","--","--","width",":","Dodging","width",",","when","different","to","the","width","of","the","individual","elements",".","This","is","useful","when","you","want","to","align","narrow","geoms","with","wider","geoms","preserve",":","Should","dodging","preserve","the","total","width","of","all","elements","at","a","position",",","or","the","width","of","a","single","element","?","padding",":","Padding","between","elements","at","the","same","position",".","Elements","are","shrunk","by","this","proportion","to","allow","space","between","them",".","reverse",":","Reverse","the","default","ordering","of","the","groups",".","This","is","useful","if","you","'re","rotating","both","the","plot","and","legend","."]
code: def strategy(data, params):
      padding = params["padding"]
      n = params["n"]
  
      if not all(col in data.columns for col in ["xmin", "xmax"]):
          data["xmin"] = data["x"]
          data["xmax"] = data["x"]
  
      # Groups of boxes that share the same position
      data["xid"] = find_x_overlaps(data)
  
      # Find newx using xid, i.e. the center of each group of
      # overlapping elements. for boxes, bars, etc. this should
      # be the same as original x, but for arbitrary rects it
      # may not be
      res1 = pivot_apply(data, "xmin", "xid", np.min)
      res2 = pivot_apply(data, "xmax", "xid", np.max)
      data["newx"] = (res1 + res2)[
          data["xid"].to_numpy()
      ].to_numpy() / 2
  
      if n is None:
          # If n is None, preserve total widths of elements at
          # each position by dividing widths by the number of
          # elements at that position
          n = data["xid"].value_counts().to_numpy()
          n = n[data.loc[:, "xid"] - 1]
          data["new_width"] = (data["xmax"] - data["xmin"]) / n
      else:
          data["new_width"] = (data["xmax"] - data["xmin"]) / n
  
      # Find the total width of each group of elements
      def sum_new_width(gdf):
          return pd.DataFrame(
              {
                  "size": [gdf["new_width"].sum()],
                  "newx": gdf["newx"].iloc[0],
              }
          )
  
      group_sizes = groupby_apply(data, "newx", sum_new_width)
  
      # Starting xmin for each group of elements
      starts = group_sizes["newx"] - (group_sizes["size"] / 2)
  
      # Set the elements in place
      for i, start in enumerate(starts, start=1):
          bool_idx = data["xid"] == i
          divisions = np.cumsum(
              np.hstack([start, data.loc[bool_idx, "new_width"]])
          )
          data.loc[bool_idx, "xmin"] = divisions[:-1]
          data.loc[bool_idx, "xmax"] = divisions[1:]
  
      # x values get moved to between xmin and xmax
      data["x"] = (data["xmin"] + data["xmax"]) / 2
  
      # Shrink elements to add space between them
      if data["xid"].duplicated().any():
          pad_width = data["new_width"] * (1 - padding)
          data["xmin"] = data["x"] - pad_width / 2
          data["xmax"] = data["x"] + pad_width / 2
  
      if "x" in data and "xend" in data:
          data["x"] = data["xmin"]
          data["xend"] = data["xmax"]
  
      data = data.drop(
          columns=["xid", "newx", "new_width"], errors="ignore"
      )
      return data
  
file_path: plotnine/plotnine/positions/position_dodge2.py
imports: from __future__ import annotations
  import typing
  from copy import copy
  import numpy
  import pandas
  from .._utils import groupby_apply, pivot_apply
  from ..exceptions import PlotnineError
  from .position_dodge import position_dodge
label_desc: Use image node_id 4 for calling the position_dodge2 obj's underlying member method code with example usage: obj.strategy(data, params) and returns: data, pd