class_docstr: Interface for Pandas GroupBy operations allowing specified group order.
  
  Writing our own class to do this has a few advantages:
  - It constrains the interface between Plot and Stat/Move objects
  - It allows control over the row order of the GroupBy result, which is
    important when using in the context of some Move operations (dodge, stack, ...)
  - It simplifies some complexities regarding the return type and Index contents
    one encounters with Pandas, especially for DataFrame -> DataFrame applies
  - It increases future flexibility regarding alternate DataFrame libraries
class_docstr_tok: ["Interface","for","Pandas","GroupBy","operations","allowing","specified","group","order",".","Writing","our","own","class","to","do","this","has","a","few","advantages",":","-","It","constrains","the","interface","between","Plot","and","Stat\/Move","objects","-","It","allows","control","over","the","row","order","of","the","GroupBy","result",",","which","is","important","when","using","in","the","context","of","some","Move","operations","(","dodge",",","stack",",","...",")","-","It","simplifies","some","complexities","regarding","the","return","type","and","Index","contents","one","encounters","with","Pandas",",","especially","for","DataFrame","-",">","DataFrame","applies","-","It","increases","future","flexibility","regarding","alternate","DataFrame","libraries"]
docstr: Return index with Cartesian product of ordered grouping variable levels.
docstr_tok: ["Return","index","with","Cartesian","product","of","ordered","grouping","variable","levels","."]
code: def _get_groups(
      self, data: DataFrame
  ) -> tuple[str | list[str], Index | MultiIndex]:
      """Return index with Cartesian product of ordered grouping variable levels."""
      levels = {}
      for var, order in self.order.items():
          if var in data:
              if order is None:
                  order = categorical_order(data[var])
              levels[var] = order
  
      grouper: str | list[str]
      groups: Index | MultiIndex
      if not levels:
          grouper = []
          groups = pd.Index([])
      elif len(levels) > 1:
          grouper = list(levels)
          groups = pd.MultiIndex.from_product(
              levels.values(), names=grouper
          )
      else:
          (grouper,) = list(levels)
          groups = pd.Index(levels[grouper], name=grouper)
      return grouper, groups
  
file_path: seaborn/seaborn/_core/groupby.py
imports: from __future__ import annotations
  from typing import cast, Iterable
  import pandas
  from seaborn._core.rules import categorical_order
  from typing import TYPE_CHECKING
label_desc: Use image node_id 2 for calling the GroupBy obj's underlying member method code with example usage: obj._get_groups(data) and returns: grouper,  groups