# Buggy code
def _factorize_keys(lk, rk, sort=True):
    if is_datetime64tz_dtype(lk) and is_datetime64tz_dtype(rk):
        lk = lk.values
        rk = rk.values

    # if we exactly match in categories, allow us to factorize on codes
    if is_categorical_dtype(lk) and is_categorical_dtype(rk) and lk.is_dtype_equal(rk):
        klass = libhashtable.Int64Factorizer

        if lk.categories.equals(rk.categories):
            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_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


# Traceback (ValueError)
In [4]: pd.merge(df, df, on='A').dtypes
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-4-e77608ca3973> in <module>()
----> 1 pd.merge(df, df, on='A').dtypes

~/pandas/pandas/core/reshape/merge.py in merge(left, right, how, on, left_on, right_on, left_index, right_index, sort, suffixes, copy, indicator, validate)
61                          copy=copy, indicator=indicator,
62                          validate=validate)
---> 63     return op.get_result()
64
65

~/pandas/pandas/core/reshape/merge.py in get_result(self)
562                 self.left, self.right)
563
--> 564         join_index, left_indexer, right_indexer = self._get_join_info()
565
566         ldata, rdata = self.left._data, self.right._data

~/pandas/pandas/core/reshape/merge.py in _get_join_info(self)
771         else:
772             (left_indexer,
--> 773              right_indexer) = self._get_join_indexers()
774
775             if self.right_index:

~/pandas/pandas/core/reshape/merge.py in _get_join_indexers(self)
750                                   self.right_join_keys,
751                                   sort=self.sort,
--> 752                                   how=self.how)
753
754     def _get_join_info(self):

~/pandas/pandas/core/reshape/merge.py in _get_join_indexers(left_keys, right_keys, sort, how, **kwargs)
1120
1121     # get left &amp; right join labels and num. of levels at each location
-> 1122     llab, rlab, shape = map(list, zip(* map(fkeys, left_keys, right_keys)))
1123
1124     # get flat i8 keys from label lists

~/pandas/pandas/core/reshape/merge.py in _factorize_keys(lk, rk, sort)
1554     elif is_int_or_datetime_dtype(lk) and is_int_or_datetime_dtype(rk):
1555         klass = libhashtable.Int64Factorizer
-> 1556         lk = ensure_int64(com.values_from_object(lk))
1557         rk = ensure_int64(com.values_from_object(rk))
1558     else:   1557         rk = ensure_int64(com.values_from_object(rk))
1558     else:

~/pandas/pandas/_libs/algos_common_helper.pxi in pandas._libs.algos.ensure_int64()

ValueError: cannot convert float NaN to integer

# Fixed 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.values
        rk = rk.values

    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