Repository: dask/dask

Issue:
Implicit data type conversion after concat
**What happened**: 
Dask implicitly converts column datatypes after DF's concat, when one of DF's is empty.

**What you expected to happen**:
Warning (at-least) or exception if dtype converted or same dtype for columns (this how it works in Pandas)

**Minimal Complete Verifiable Example**:

```python

import pandas as pd
import dask.dataframe as dd

sample_data = [
  {'col_int64': 100},
  {'col_int64': 200},
  {'col_int64': 300},
]

def main():

  # let's dummy pandas df with int64 col
  pandas_df = pd.DataFrame(sample_data, dtype='int64')
  assert pandas_df.dtypes['col_int64'] == 'int64'

  print('\nPandas :: Types before DF concat:')
  print(pandas_df.dtypes)
  print()

  # init empty pandas dataframe
  empty_pandas_df = pd.DataFrame([])

  # concat dummy dataframe with empty
  df = pd.concat([pandas_df, empty_pandas_df])
  assert df.dtypes['col_int64'] == 'int64'

  print('\nPandas :: after DF concat:')
  print(df.dtypes)
  print()

  # now let's try Dask
  dask_df = dd.from_pandas(pandas_df, npartitions=1)
  assert dask_df.dtypes['col_int64'] == 'int64'

  print('\nDask :: Types before DF concat:')
  print(dask_df.dtypes)
  print()

  # init empty dask
  empty_dask_df = dd.from_pandas(empty_pandas_df, npartitions=1)

  # concat with empty
  df = dd.concat([dask_df, empty_dask_df])
  print('\nDask :: Types after DF concat:')
  print(df.dtypes)
  print()
  if df.dtypes['col_int64'] != 'int64':
    print(f'Silently converted to {df.dtypes["col_int64"]}\n\n')

main()

### output:

# Pandas :: Types before DF concat:
# col_int64    int64
# dtype: object


# Pandas :: after DF concat:
# col_int64    int64
# dtype: object


# Dask :: Types before DF concat:
# col_int64    int64
# dtype: object


# Dask :: Types after DF concat:
# col_int64    float64
# dtype: object

# Silently converted to float64

```

**Anything else we need to know?**:

**Environment**:

- Dask version:  2022.5.0
- Python version: 3.9.10
- Operating System: OSX
- Install method (conda, pip, source): pip


Discussion:
@patefon Thanks for reporting this! I'm able to reproduce this, and it looks like a bug.

Resolving patch:
diff --git a/dask/dataframe/multi.py b/dask/dataframe/multi.py
--- a/dask/dataframe/multi.py
+++ b/dask/dataframe/multi.py
@@ -1237,6 +1237,12 @@ def concat(
         raise ValueError("'join' must be 'inner' or 'outer'")
 
     axis = DataFrame._validate_axis(axis)
+    try:
+        # remove any empty DataFrames
+        dfs = [df for df in dfs if bool(len(df.columns))]
+    except AttributeError:
+        # 'Series' object has no attribute 'columns'
+        pass
     dasks = [df for df in dfs if isinstance(df, _Frame)]
     dfs = _maybe_from_pandas(dfs)
 


Test patch:
diff --git a/dask/dataframe/tests/test_multi.py b/dask/dataframe/tests/test_multi.py
--- a/dask/dataframe/tests/test_multi.py
+++ b/dask/dataframe/tests/test_multi.py
@@ -796,6 +796,29 @@ def test_concat_with_operation_remains_hlg():
     assert_eq(result, expected)
 
 
+def test_concat_dataframe_empty():
+    df = pd.DataFrame({"a": [100, 200, 300]}, dtype="int64")
+    empty_df = pd.DataFrame([], dtype="int64")
+    df_concat = pd.concat([df, empty_df])
+
+    ddf = dd.from_pandas(df, npartitions=1)
+    empty_ddf = dd.from_pandas(empty_df, npartitions=1)
+    ddf_concat = dd.concat([ddf, empty_ddf])
+    assert_eq(df_concat, ddf_concat)
+
+    empty_df_with_col = pd.DataFrame([], columns=["x"], dtype="int64")
+    df_concat_with_col = pd.concat([df, empty_df_with_col])
+    empty_ddf_with_col = dd.from_pandas(empty_df_with_col, npartitions=1)
+    ddf_concat_with_col = dd.concat([ddf, empty_ddf_with_col])
+    # NOTE: empty_ddf_with_col.index.dtype == dtype('O') while ddf.index.dtype == dtype('int64')
+    # when we concatenate the resulting ddf_concat_with_col.index.dtype == dtype('O'). However,
+    # the pandas version is smart enough to identify the empty rows and assign dtype('int64'),
+    # which causes assert_eq to fail when checking dtype. Hence the follwoing line.
+    ddf_concat_with_col._meta.index = ddf_concat_with_col._meta.index.astype("int64")
+
+    assert_eq(df_concat_with_col, ddf_concat_with_col, check_dtype=False)
+
+
 @pytest.mark.parametrize(
     "value_1, value_2",
     [