code: def _factorize_keys(lk, rk, sort=True):
      # Some pre-processing for non-ndarray lk / rk
      if is_datetime64tz_dtype(lk) and is_datetime64tz_dtype(rk):
          lk = lk._data
          rk = rk._data
  
      elif (
          is_categorical_dtype(lk)
          and is_categorical_dtype(rk)
          and lk.is_dtype_equal(rk)
      ):
          if lk.categories.equals(rk.categories):
              # if we exactly match in categories, allow us to factorize on codes
              rk = rk.codes
          else:
              # Same categories in different orders -> recode
              rk = _recode_for_categories(
                  rk.codes, rk.categories, lk.categories
              )
  
          lk = ensure_int64(lk.codes)
          rk = ensure_int64(rk)
  
      elif (
          is_extension_array_dtype(lk.dtype)
          and is_extension_array_dtype(rk.dtype)
          and lk.dtype == rk.dtype
      ):
          lk, _ = lk._values_for_factorize()
          rk, _ = rk._values_for_factorize()
  
      if is_integer_dtype(lk) and is_integer_dtype(rk):
          # GH#23917 TODO: needs tests for case where lk is integer-dtype
          #  and rk is datetime-dtype
          klass = libhashtable.Int64Factorizer
          lk = ensure_int64(com.values_from_object(lk))
          rk = ensure_int64(com.values_from_object(rk))
      elif issubclass(
          lk.dtype.type, (np.timedelta64, np.datetime64)
      ) and issubclass(rk.dtype.type, (np.timedelta64, np.datetime64)):
          # GH#23917 TODO: Needs tests for non-matching dtypes
          klass = libhashtable.Int64Factorizer
          lk = ensure_int64(com.values_from_object(lk))
          rk = ensure_int64(com.values_from_object(rk))
      else:
          klass = libhashtable.Factorizer
          lk = ensure_object(lk)
          rk = ensure_object(rk)
  
      rizer = klass(max(len(lk), len(rk)))
  
      llab = rizer.factorize(lk)
      rlab = rizer.factorize(rk)
  
      count = rizer.get_count()
  
      if sort:
          uniques = rizer.uniques.to_array()
          llab, rlab = _sort_labels(uniques, llab, rlab)
  
      # NA group
      lmask = llab == -1
      lany = lmask.any()
      rmask = rlab == -1
      rany = rmask.any()
  
      if lany or rany:
          if lany:
              np.putmask(llab, lmask, count)
          if rany:
              np.putmask(rlab, rmask, count)
          count += 1
  
      return llab, rlab, count
  
imports: import copy
  import string
  import warnings
  import numpy
  from pandas._libs import hashtable, join, lib
  import pandas.compat
  from pandas.compat import filter, lzip, map, range, zip
  from pandas.errors import MergeError
  from pandas.util._decorators import Appender, Substitution
  from pandas.core.dtypes.common import ensure_float64, ensure_int64, ensure_object, is_array_like, is_bool, is_bool_dtype, is_categorical_dtype, is_datetime64_dtype, is_datetime64tz_dtype, is_datetimelike, is_dtype_equal, is_extension_array_dtype, is_float_dtype, is_int64_dtype, is_integer, is_integer_dtype, is_list_like, is_number, is_numeric_dtype, is_object_dtype, needs_i8_conversion
  from pandas.core.dtypes.missing import isnull, na_value_for_dtype
  from pandas import Categorical, DataFrame, Index, MultiIndex, Series, Timedelta
  import pandas.core.algorithms
  from pandas.core.arrays.categorical import _recode_for_categories
  import pandas.core.common
  from pandas.core.frame import _merge_doc
  from pandas.core.internals import concatenate_block_managers, items_overlap_with_suffix
  import pandas.core.sorting
  from pandas.core.sorting import is_int64_overflow_possible
label_desc: Use image node_id 14 for calling a global function with example usage: _factorize_keys(lk, rk, sort) and returns: llab,  rlab,  count