Repository: pandas-dev/pandas

Issue:
DataFrame.merge with empty frame and Int64 column gives object dtype
#### Code Sample, a copy-pastable example if possible

```python
x = pd.DataFrame({
    'date': pd.Series([pd.Timestamp('2018-01-01')]),
    'value': pd.Series([1], dtype='Int64')
})
y = x[:0]
z = x.merge(y, on='date', how='left')

# z
        date  value_x value_y
0 2018-01-01        1     NaN

# z.dtypes

date       datetime64[ns]
value_x             Int64
value_y            object
dtype: object
```
#### Problem descriptioy

The dtype of the `value_y` column should be `Int64`. 

#### Expected Output

```python
date       datetime64[ns]
value_x             Int64
value_y             Int64
dtype: object
```

#### Output of ``pd.show_versions()``

<details>

INSTALLED VERSIONS
------------------
commit: None
python: 3.6.8.final.0
python-bits: 64
OS: Linux
OS-release: 4.15.0-43-generic
machine: x86_64
processor: x86_64
byteorder: little
LC_ALL: C.UTF-8
LANG: C.UTF-8
LOCALE: en_US.UTF-8
pandas: 0.24.1
pytest: 4.1.1
pip: 18.1
setuptools: 40.6.3
Cython: 0.29.2
numpy: 1.15.4
scipy: None
pyarrow: None
xarray: None
IPython: 7.2.0
sphinx: 1.8.2
patsy: None
dateutil: 2.7.5
pytz: 2018.9
blosc: None
bottleneck: None
tables: 3.4.4
numexpr: 2.6.9
feather: None
matplotlib: 3.0.2
openpyxl: None
xlrd: None
xlwt: None
xlsxwriter: None
lxml.etree: 4.3.0
bs4: None
html5lib: None
sqlalchemy: 1.2.16
pymysql: None
psycopg2: None
jinja2: 2.10
s3fs: None
fastparquet: None
pandas_gbq: None
pandas_datareader: None
gcsfs: None

</details>



Discussion:
@josham : Given that you have a `NaN` in `value_y`, it would be impossible to have `Int64` (would be a floating dtype actually).  That being said, uncertain as to why you're getting `object` though...

cc @jreback 
I agree that this should be able to return Int types here; this was just put in place in 0.24 so may not be well handled for empty types
@jreback I came across another merge issue with Int64. I can add to this issue or create another, let me know what you prefer.

```python
x = pd.DataFrame({
    'date': [pd.Timestamp('2018-01-01')],
    'value': pd.Series([np.nan], dtype='Int64')
})
y = pd.DataFrame({
    'date': [pd.Timestamp('2018-01-01')],
    'value': pd.Series([4], dtype='Int64')
})
z = x.merge(y, on=['date'])
```
```python
Traceback (most recent call last):
  File "scratch.py", line 23, in <module>
    z = x.merge(y, on=['date'])
  File "/python/lib/python3.6/site-packages/pandas/core/frame.py", line 6868, in merge
    copy=copy, indicator=indicator, validate=validate)
  File "/python/lib/python3.6/site-packages/pandas/core/reshape/merge.py", line 48, in merge
    return op.get_result()
  File "/python/lib/python3.6/site-packages/pandas/core/reshape/merge.py", line 560, in get_result
    concat_axis=0, copy=self.copy)
  File "/python/lib/python3.6/site-packages/pandas/core/internals/managers.py", line 2061, in concatenate_block_managers
    concatenate_join_units(join_units, concat_axis, copy=copy),
  File "/python/lib/python3.6/site-packages/pandas/core/internals/concat.py", line 240, in concatenate_join_units
    for ju in join_units]
  File "/python/lib/python3.6/site-packages/pandas/core/internals/concat.py", line 240, in <listcomp>
    for ju in join_units]
  File "/python/lib/python3.6/site-packages/pandas/core/internals/concat.py", line 194, in get_reindexed_values
    missing_arr = np.empty(self.shape, dtype=empty_dtype)
TypeError: data type not understood
```
@jreback, @TomAugspurger the following seems to fix both issues. Does this seem correct to you?

```
--- a/pandas/core/internals/concat.py
+++ b/pandas/core/internals/concat.py
@@ -190,6 +190,8 @@ class JoinUnit(object):
                     pass
                 elif getattr(self.block, 'is_sparse', False):
                     pass
+                elif getattr(self.block, 'is_extension', False):
+                    pass
                 else:
                     missing_arr = np.empty(self.shape, dtype=empty_dtype)
                     missing_arr.fill(fill_value)
```

Resolving patch:
diff --git a/pandas/core/internals/concat.py b/pandas/core/internals/concat.py
--- a/pandas/core/internals/concat.py
+++ b/pandas/core/internals/concat.py
@@ -190,6 +190,8 @@ def get_reindexed_values(self, empty_dtype, upcasted_na):
                     pass
                 elif getattr(self.block, 'is_sparse', False):
                     pass
+                elif getattr(self.block, 'is_extension', False):
+                    pass
                 else:
                     missing_arr = np.empty(self.shape, dtype=empty_dtype)
                     missing_arr.fill(fill_value)


Test patch:
diff --git a/pandas/tests/reshape/merge/test_merge.py b/pandas/tests/reshape/merge/test_merge.py
--- a/pandas/tests/reshape/merge/test_merge.py
+++ b/pandas/tests/reshape/merge/test_merge.py
@@ -39,6 +39,54 @@ def get_test_data(ngroups=NGROUPS, n=N):
     return arr
 
 
+def get_series():
+    return [
+        pd.Series([1], dtype='int64'),
+        pd.Series([1], dtype='Int64'),
+        pd.Series([1.23]),
+        pd.Series(['foo']),
+        pd.Series([True]),
+        pd.Series([pd.Timestamp('2018-01-01')]),
+        pd.Series([pd.Timestamp('2018-01-01', tz='US/Eastern')]),
+    ]
+
+
+def get_series_na():
+    return [
+        pd.Series([np.nan], dtype='Int64'),
+        pd.Series([np.nan], dtype='float'),
+        pd.Series([np.nan], dtype='object'),
+        pd.Series([pd.NaT]),
+    ]
+
+
+@pytest.fixture(params=get_series(), ids=lambda x: x.dtype.name)
+def series_of_dtype(request):
+    """
+    A parametrized fixture returning a variety of Series of different
+    dtypes
+    """
+    return request.param
+
+
+@pytest.fixture(params=get_series(), ids=lambda x: x.dtype.name)
+def series_of_dtype2(request):
+    """
+    A duplicate of the series_of_dtype fixture, so that it can be used
+    twice by a single function
+    """
+    return request.param
+
+
+@pytest.fixture(params=get_series_na(), ids=lambda x: x.dtype.name)
+def series_of_dtype_all_na(request):
+    """
+    A parametrized fixture returning a variety of Series with all NA
+    values
+    """
+    return request.param
+
+
 class TestMerge(object):
 
     def setup_method(self, method):
@@ -428,6 +476,36 @@ def check2(exp, kwarg):
                 check1(exp_in, kwarg)
                 check2(exp_out, kwarg)
 
+    def test_merge_empty_frame(self, series_of_dtype, series_of_dtype2):
+        # GH 25183
+        df = pd.DataFrame({'key': series_of_dtype, 'value': series_of_dtype2},
+                          columns=['key', 'value'])
+        df_empty = df[:0]
+        expected = pd.DataFrame({
+            'value_x': pd.Series(dtype=df.dtypes['value']),
+            'key': pd.Series(dtype=df.dtypes['key']),
+            'value_y': pd.Series(dtype=df.dtypes['value']),
+        }, columns=['value_x', 'key', 'value_y'])
+        actual = df_empty.merge(df, on='key')
+        assert_frame_equal(actual, expected)
+
+    def test_merge_all_na_column(self, series_of_dtype,
+                                 series_of_dtype_all_na):
+        # GH 25183
+        df_left = pd.DataFrame(
+            {'key': series_of_dtype, 'value': series_of_dtype_all_na},
+            columns=['key', 'value'])
+        df_right = pd.DataFrame(
+            {'key': series_of_dtype, 'value': series_of_dtype_all_na},
+            columns=['key', 'value'])
+        expected = pd.DataFrame({
+            'key': series_of_dtype,
+            'value_x': series_of_dtype_all_na,
+            'value_y': series_of_dtype_all_na,
+        }, columns=['key', 'value_x', 'value_y'])
+        actual = df_left.merge(df_right, on='key')
+        assert_frame_equal(actual, expected)
+
     def test_merge_nosort(self):
         # #2098, anything to do?