Repository: pandas-dev/pandas
Pull request: BUG: fix tzaware dataframe transpose bug
This allows us to get rid of a bunch of xfails and FIXMEs in arithmetic tests.  Changes in groupby are the most likely to need attention; this is not an area of the code I know well.

Not sure if TestTranspose belongs somewhere else.  Suggestions welcome.

Split a couple of too-widely-scoped groupby tests.

Will follow-up with issues this closes, whatsnew entry, and GH references added to tests.

xref #23988

- [x] closes #13287
- [x] tests added / passed
- [x] passes `git diff upstream/master -u -- "*.py" | flake8 --diff`
- [x] whatsnew entry


Problem statement:
BUG: Pandas cannot create DataFrame from Numpy Array of TimeStamps
I have the following array of Timestamps:

``` python
ts_array = np.array([[Timestamp('2016-05-02 15:50:00+0000', tz='UTC', offset='5T'),
        Timestamp('2016-05-02 15:50:00+0000', tz='UTC', offset='5T'),
        Timestamp('2016-05-02 15:50:00+0000', tz='UTC', offset='5T')],
       [Timestamp('2016-05-02 17:10:00+0000', tz='UTC', offset='5T'),
        Timestamp('2016-05-02 17:10:00+0000', tz='UTC', offset='5T'),
        Timestamp('2016-05-02 17:10:00+0000', tz='UTC', offset='5T')],
       [Timestamp('2016-05-02 20:25:00+0000', tz='UTC', offset='5T'),
        Timestamp('2016-05-02 20:25:00+0000', tz='UTC', offset='5T'),
        Timestamp('2016-05-02 20:25:00+0000', tz='UTC', offset='5T')]], dtype=object)
```

I can't create a DataFrame from this array using the DataFrame constructor:

``` python
pd.DataFrame(ts_array)
```

```
Traceback (most recent call last):
  File "/Users/jkelleher/anaconda/lib/python2.7/site-packages/IPython/core/interactiveshell.py", line 2885, in run_code
    exec(code_obj, self.user_global_ns, self.user_ns)
  File "<ipython-input-46-ae20c6b6248f>", line 1, in <module>
    pd.DataFrame(ts_array)
  File "/Users/jkelleher/anaconda/lib/python2.7/site-packages/pandas/core/frame.py", line 255, in __init__
    copy=copy)
  File "/Users/jkelleher/anaconda/lib/python2.7/site-packages/pandas/core/frame.py", line 432, in _init_ndarray
    return create_block_manager_from_blocks([values], [columns, index])
  File "/Users/jkelleher/anaconda/lib/python2.7/site-packages/pandas/core/internals.py", line 3986, in create_block_manager_from_blocks
    mgr = BlockManager(blocks, axes)
  File "/Users/jkelleher/anaconda/lib/python2.7/site-packages/pandas/core/internals.py", line 2591, in __init__
    (block.ndim, self.ndim))
AssertionError: Number of Block dimensions (1) must equal number of axes (2)
```

I can create the DataFrame from the array using `from_records`:

``` python
ts_df = pd.DataFrame.from_records(ts_array)
```

However, when I attempt to transpose this DataFrame, I wind up with the same `AssertionError` as before.

```
AssertionError: Number of Block dimensions (1) must equal number of axes (2)
```

If I convert the Timestamps to Datetimes, the error persists. I can, however, convert the Timestamps to Datetime64 objects, and this fixes the problem.

``` python
dt64_array = np.array([[ts.to_datetime64() for ts in sublist] for sublist in ts_array])
pd.DataFrame(dt64_array)
```

```
Out[56]: 
                    0                   1                   2
0 2016-05-02 15:50:00 2016-05-02 15:50:00 2016-05-02 15:50:00
1 2016-05-02 17:10:00 2016-05-02 17:10:00 2016-05-02 17:10:00
2 2016-05-02 20:25:00 2016-05-02 20:25:00 2016-05-02 20:25:00
```

``` python
pd.DataFrame(dt64_array).transpose()
```

```
Out[57]: 
                    0                   1                   2
0 2016-05-02 15:50:00 2016-05-02 17:10:00 2016-05-02 20:25:00
1 2016-05-02 15:50:00 2016-05-02 17:10:00 2016-05-02 20:25:00
2 2016-05-02 15:50:00 2016-05-02 17:10:00 2016-05-02 20:25:00
```

Though I found a suitable workaround, I feel like pandas should be able to construct and operate on DataFrames of Timestamps as easily as other other objects.
#### output of `pd.show_versions()`

```
INSTALLED VERSIONS
------------------
commit: None
python: 2.7.11.final.0
python-bits: 64
OS: Darwin
OS-release: 15.5.0
machine: x86_64
processor: i386
byteorder: little
LC_ALL: None
LANG: None

pandas: 0.18.1
nose: 1.3.7
pip: 8.1.2
setuptools: 20.3
Cython: 0.24
numpy: 1.11.0
scipy: 0.17.1
statsmodels: 0.8.0.dev0+970e99e
xarray: None
IPython: 4.1.2
sphinx: 1.3.5
patsy: 0.4.0
dateutil: 2.5.3
pytz: 2016.4
blosc: None
bottleneck: 1.0.0
tables: 3.2.2
numexpr: 2.5
matplotlib: 1.5.1
openpyxl: 2.3.2
xlrd: 0.9.4
xlwt: 1.0.0
xlsxwriter: 0.8.4
lxml: 3.6.0
bs4: 4.4.1
html5lib: None
httplib2: None
apiclient: None
sqlalchemy: 1.0.12
pymysql: None
psycopg2: None
jinja2: 2.8
boto: 2.39.0
pandas_datareader: None
```

Hints from issue discussion:
```
In [4]: DataFrame.from_records(ts_array)
Out[4]: 
                          0                         1                         2
0 2016-05-02 15:50:00+00:00 2016-05-02 15:50:00+00:00 2016-05-02 15:50:00+00:00
1 2016-05-02 17:10:00+00:00 2016-05-02 17:10:00+00:00 2016-05-02 17:10:00+00:00
2 2016-05-02 20:25:00+00:00 2016-05-02 20:25:00+00:00 2016-05-02 20:25:00+00:00
```

I suppose its a bug, but you are just going about this the wrong way to have a 2- d numpy array of Timestamps (which is completely inefficient) THEN create a frame.

yeah these are stored internally in a different way, so I guess `.T` is broken on these types of things.

If you want to step thru and submit a PR have at it.

Commit message: add whatsnew

Patch to review:
diff --git a/doc/source/whatsnew/v0.25.0.rst b/doc/source/whatsnew/v0.25.0.rst
index 76ee21b4c9a50..52bcf2e26e165 100644
--- a/doc/source/whatsnew/v0.25.0.rst
+++ b/doc/source/whatsnew/v0.25.0.rst
@@ -720,6 +720,7 @@ Reshaping
 - Bug in :func:`pandas.cut` where large bins could incorrectly raise an error due to an integer overflow (:issue:`26045`)
 - Bug in :func:`DataFrame.sort_index` where an error is thrown when a multi-indexed ``DataFrame`` is sorted on all levels with the initial level sorted last (:issue:`26053`)
 - Bug in :meth:`Series.nlargest` treats ``True`` as smaller than ``False`` (:issue:`26154`)
+- Bug in :meth:`DataFrame.transpose` where transposing a DataFrame with a timezone-aware datetime column would incorrectly raise ``ValueError`` (:issue:`26825`)
 
 Sparse
 ^^^^^^
@@ -735,6 +736,7 @@ Other
 - Removed unused C functions from vendored UltraJSON implementation (:issue:`26198`)
 - Bug in :func:`factorize` when passing an ``ExtensionArray`` with a custom ``na_sentinel`` (:issue:`25696`).
 - Allow :class:`Index` and :class:`RangeIndex` to be passed to numpy ``min`` and ``max`` functions (:issue:`26125`)
+- Bug in :class:`DataFrame` where passing an object array of timezone-aware `datetime` objects would incorrectly raise ``ValueError`` (:issue:`13287`)
 
 .. _whatsnew_0.250.contributors:
 
diff --git a/pandas/core/groupby/generic.py b/pandas/core/groupby/generic.py
index 57d14cb4c15d7..3b86ec982cde5 100644
--- a/pandas/core/groupby/generic.py
+++ b/pandas/core/groupby/generic.py
@@ -288,7 +288,6 @@ def _decide_output_index(self, output, labels):
 
     def _wrap_applied_output(self, keys, values, not_indexed_same=False):
         from pandas.core.index import _all_indexes_same
-        from pandas.core.tools.numeric import to_numeric
 
         if len(keys) == 0:
             return DataFrame(index=keys)
@@ -360,7 +359,6 @@ def first_not_none(values):
                     # provide a reduction (Frame -> Series) if groups are
                     # unique
                     if self.squeeze:
-
                         # assign the name to this series
                         if singular_series:
                             values[0].name = keys[0]
@@ -434,15 +432,8 @@ def first_not_none(values):
                 # if we have date/time like in the original, then coerce dates
                 # as we are stacking can easily have object dtypes here
                 so = self._selected_obj
-                if (so.ndim == 2 and so.dtypes.apply(is_datetimelike).any()):
-                    result = result.apply(
-                        lambda x: to_numeric(x, errors='ignore'))
-                    date_cols = self._selected_obj.select_dtypes(
-                        include=['datetime', 'timedelta']).columns
-                    date_cols = date_cols.intersection(result.columns)
-                    result[date_cols] = (result[date_cols]
-                                         ._convert(datetime=True,
-                                                   coerce=True))
+                if so.ndim == 2 and so.dtypes.apply(is_datetimelike).any():
+                    _recast_datetimelike_result(result)
                 else:
                     result = result._convert(datetime=True)
 
@@ -1664,3 +1655,36 @@ def _normalize_keyword_aggregation(kwargs):
         order.append((column,
                       com.get_callable_name(aggfunc) or aggfunc))
     return aggspec, columns, order
+
+
+def _recast_datetimelike_result(result):
+    """
+    If we have date/time like in the original, then coerce dates
+    as we are stacking can easily have object dtypes here.
+
+    Parameters
+    ----------
+    result : DataFrame
+
+    Notes
+    -----
+    - Assumes Groupby._selected_obj has ndim==2 and at least one
+    datetimelike column
+    - Modifies `result` inplace
+    """
+    ocols = [idx for idx in range(len(result.columns))
+             if result.dtypes[idx] == object]
+
+    for cidx in ocols:
+        # TODO: get maybe_convert_objects working here
+        cvals = result.iloc[:, cidx]
+
+        # TODO: should we use skipna=True?
+        exdtype = lib.infer_dtype(cvals.values, skipna=False)
+        if exdtype == 'integer':
+            result.iloc[:, cidx] = cvals.astype(np.int64)
+        if exdtype in ['float', 'mixed-integer-float']:
+            result.iloc[:, cidx] = cvals.astype(np.float64)
+        if exdtype == 'datetime':
+            # TODO: what about z-aware?
+            result.iloc[:, cidx] = cvals.astype('M8[ns]')
diff --git a/pandas/core/internals/construction.py b/pandas/core/internals/construction.py
index 863b9f7fb16d7..cebeb025e1cdd 100644
--- a/pandas/core/internals/construction.py
+++ b/pandas/core/internals/construction.py
@@ -160,7 +160,29 @@ def init_ndarray(values, index, columns, dtype=None, copy=False):
     # on the entire block; this is to convert if we have datetimelike's
     # embedded in an object type
     if dtype is None and is_object_dtype(values):
-        values = maybe_infer_to_datetimelike(values)
+
+        if values.ndim == 2 and values.shape[0] != 1:
+            # kludge to transpose and separate blocks
+            # unnecessary if we ever allow 2D DatetimeArray
+
+            dvals_list = [maybe_infer_to_datetimelike(values[n, :])
+                          for n in range(len(values))]
+            for n in range(len(dvals_list)):
+                if isinstance(dvals_list[n], np.ndarray):
+                    dvals_list[n] = dvals_list[n].reshape(1, -1)
+
+            from pandas.core.internals.blocks import make_block
+
+            # TODO: What about re-joining object columns?
+            bdvals = [make_block(dvals_list[n], placement=[n])
+                      for n in range(len(dvals_list))]
+            return create_block_manager_from_blocks(bdvals,
+                                                    [columns, index])
+
+        else:
+            dvals = maybe_infer_to_datetimelike(values)
+
+        values = dvals
 
     return create_block_manager_from_blocks([values], [columns, index])
 
diff --git a/pandas/tests/arithmetic/test_datetime64.py b/pandas/tests/arithmetic/test_datetime64.py
index afd29852fea7e..cc9940a2c5524 100644
--- a/pandas/tests/arithmetic/test_datetime64.py
+++ b/pandas/tests/arithmetic/test_datetime64.py
@@ -438,8 +438,7 @@ def test_dti_cmp_null_scalar_inequality(self, tz_naive_fixture, other,
         # GH#19301
         tz = tz_naive_fixture
         dti = pd.date_range('2016-01-01', periods=2, tz=tz)
-        # FIXME: ValueError with transpose
-        dtarr = tm.box_expected(dti, box_with_array, transpose=False)
+        dtarr = tm.box_expected(dti, box_with_array)
         msg = 'Invalid comparison between'
         with pytest.raises(TypeError, match=msg):
             dtarr < other
@@ -592,15 +591,15 @@ def test_comparison_tzawareness_compat(self, op, box_with_array):
         dr = pd.date_range('2016-01-01', periods=6)
         dz = dr.tz_localize('US/Pacific')
 
-        # FIXME: ValueError with transpose
-        dr = tm.box_expected(dr, box_with_array, transpose=False)
-        dz = tm.box_expected(dz, box_with_array, transpose=False)
+        dr = tm.box_expected(dr, box_with_array)
+        dz = tm.box_expected(dz, box_with_array)
 
         msg = 'Cannot compare tz-naive and tz-aware'
         with pytest.raises(TypeError, match=msg):
             op(dr, dz)
         if box_with_array is not pd.DataFrame:
-            # DataFrame op is invalid until transpose bug is fixed
+            # FIXME: DataFrame case fails to raise for == and !=, wrong
+            #  message for inequalities
             with pytest.raises(TypeError, match=msg):
                 op(dr, list(dz))
             with pytest.raises(TypeError, match=msg):
@@ -609,7 +608,8 @@ def test_comparison_tzawareness_compat(self, op, box_with_array):
         with pytest.raises(TypeError, match=msg):
             op(dz, dr)
         if box_with_array is not pd.DataFrame:
-            # DataFrame op is invalid until transpose bug is fixed
+            # FIXME: DataFrame case fails to raise for == and !=, wrong
+            #  message for inequalities
             with pytest.raises(TypeError, match=msg):
                 op(dz, list(dr))
             with pytest.raises(TypeError, match=msg):
@@ -620,8 +620,8 @@ def test_comparison_tzawareness_compat(self, op, box_with_array):
         assert_all(dr == dr)
         assert_all(dz == dz)
         if box_with_array is not pd.DataFrame:
-            # DataFrame doesn't align the lists correctly unless we transpose,
-            #  which we cannot do at the moment
+            # FIXME: DataFrame case fails to raise for == and !=, wrong
+            #  message for inequalities
             assert (dr == list(dr)).all()
             assert (dz == list(dz)).all()
 
@@ -652,8 +652,7 @@ def test_scalar_comparison_tzawareness(self, op, other, tz_aware_fixture,
         tz = tz_aware_fixture
         dti = pd.date_range('2016-01-01', periods=2, tz=tz)
 
-        # FIXME: ValueError with transpose
-        dtarr = tm.box_expected(dti, box_with_array, transpose=False)
+        dtarr = tm.box_expected(dti, box_with_array)
         msg = 'Cannot compare tz-naive and tz-aware'
         with pytest.raises(TypeError, match=msg):
             op(dtarr, other)
@@ -715,17 +714,16 @@ def test_dt64arr_cmp_scalar_invalid(self, other, tz_naive_fixture,
         xbox = box_with_array if box_with_array is not pd.Index else np.ndarray
 
         rng = date_range('1/1/2000', periods=10, tz=tz)
-        # FIXME: ValueError with transpose
-        rng = tm.box_expected(rng, box_with_array, transpose=False)
+        rng = tm.box_expected(rng, box_with_array)
 
         result = rng == other
         expected = np.array([False] * 10)
-        expected = tm.box_expected(expected, xbox, transpose=False)
+        expected = tm.box_expected(expected, xbox)
         tm.assert_equal(result, expected)
 
         result = rng != other
         expected = np.array([True] * 10)
-        expected = tm.box_expected(expected, xbox, transpose=False)
+        expected = tm.box_expected(expected, xbox)
         tm.assert_equal(result, expected)
         msg = 'Invalid comparison between'
         with pytest.raises(TypeError, match=msg):
@@ -816,9 +814,8 @@ def test_dt64arr_add_timedeltalike_scalar(self, tz_naive_fixture,
         expected = pd.date_range('2000-01-01 02:00',
                                  '2000-02-01 02:00', tz=tz)
 
-        # FIXME: calling with transpose=True raises ValueError
-        rng = tm.box_expected(rng, box_with_array, transpose=False)
-        expected = tm.box_expected(expected, box_with_array, transpose=False)
+        rng = tm.box_expected(rng, box_with_array)
+        expected = tm.box_expected(expected, box_with_array)
 
         result = rng + two_hours
         tm.assert_equal(result, expected)
@@ -831,9 +828,8 @@ def test_dt64arr_iadd_timedeltalike_scalar(self, tz_naive_fixture,
         expected = pd.date_range('2000-01-01 02:00',
                                  '2000-02-01 02:00', tz=tz)
 
-        # FIXME: calling with transpose=True raises ValueError
-        rng = tm.box_expected(rng, box_with_array, transpose=False)
-        expected = tm.box_expected(expected, box_with_array, transpose=False)
+        rng = tm.box_expected(rng, box_with_array)
+        expected = tm.box_expected(expected, box_with_array)
 
         rng += two_hours
         tm.assert_equal(rng, expected)
@@ -846,9 +842,8 @@ def test_dt64arr_sub_timedeltalike_scalar(self, tz_naive_fixture,
         expected = pd.date_range('1999-12-31 22:00',
                                  '2000-01-31 22:00', tz=tz)
 
-        # FIXME: calling with transpose=True raises ValueError
-        rng = tm.box_expected(rng, box_with_array, transpose=False)
-        expected = tm.box_expected(expected, box_with_array, transpose=False)
+        rng = tm.box_expected(rng, box_with_array)
+        expected = tm.box_expected(expected, box_with_array)
 
         result = rng - two_hours
         tm.assert_equal(result, expected)
@@ -861,9 +856,8 @@ def test_dt64arr_isub_timedeltalike_scalar(self, tz_naive_fixture,
         expected = pd.date_range('1999-12-31 22:00',
                                  '2000-01-31 22:00', tz=tz)
 
-        # FIXME: calling with transpose=True raises ValueError
-        rng = tm.box_expected(rng, box_with_array, transpose=False)
-        expected = tm.box_expected(expected, box_with_array, transpose=False)
+        rng = tm.box_expected(rng, box_with_array)
+        expected = tm.box_expected(expected, box_with_array)
 
         rng -= two_hours
         tm.assert_equal(rng, expected)
@@ -918,9 +912,6 @@ def test_dt64arr_add_sub_td64_nat(self, box_with_array, tz_naive_fixture):
 
     def test_dt64arr_add_sub_td64ndarray(self, tz_naive_fixture,
                                          box_with_array):
-        if box_with_array is pd.DataFrame:
-            pytest.xfail("FIXME: ValueError with transpose; "
-                         "alignment error without")
 
         tz = tz_naive_fixture
         dti = pd.date_range('2016-01-01', periods=3, tz=tz)
@@ -942,7 +933,7 @@ def test_dt64arr_add_sub_td64ndarray(self, tz_naive_fixture,
 
         result = dtarr - tdarr
         tm.assert_equal(result, expected)
-        msg = 'cannot subtract'
+        msg = 'cannot subtract|bad operand type for unary -'
         with pytest.raises(TypeError, match=msg):
             tdarr - dtarr
 
@@ -987,13 +978,11 @@ def test_dt64arr_sub_timestamp(self, box_with_array):
                             tz='US/Eastern')
         ts = ser[0]
 
-        # FIXME: transpose raises ValueError
-        ser = tm.box_expected(ser, box_with_array, transpose=False)
+        ser = tm.box_expected(ser, box_with_array)
 
         delta_series = pd.Series([np.timedelta64(0, 'D'),
                                   np.timedelta64(1, 'D')])
-        expected = tm.box_expected(delta_series, box_with_array,
-                                   transpose=False)
+        expected = tm.box_expected(delta_series, box_with_array)
 
         tm.assert_equal(ser - ts, expected)
         tm.assert_equal(ts - ser, -expected)
@@ -1001,20 +990,19 @@ def test_dt64arr_sub_timestamp(self, box_with_array):
     def test_dt64arr_sub_NaT(self, box_with_array):
         # GH#18808
         dti = pd.DatetimeIndex([pd.NaT, pd.Timestamp('19900315')])
-        ser = tm.box_expected(dti, box_with_array, transpose=False)
+        ser = tm.box_expected(dti, box_with_array)
 
         result = ser - pd.NaT
         expected = pd.Series([pd.NaT, pd.NaT], dtype='timedelta64[ns]')
-        # FIXME: raises ValueError with transpose
-        expected = tm.box_expected(expected, box_with_array, transpose=False)
+        expected = tm.box_expected(expected, box_with_array)
         tm.assert_equal(result, expected)
 
         dti_tz = dti.tz_localize('Asia/Tokyo')
-        ser_tz = tm.box_expected(dti_tz, box_with_array, transpose=False)
+        ser_tz = tm.box_expected(dti_tz, box_with_array)
 
         result = ser_tz - pd.NaT
         expected = pd.Series([pd.NaT, pd.NaT], dtype='timedelta64[ns]')
-        expected = tm.box_expected(expected, box_with_array, transpose=False)
+        expected = tm.box_expected(expected, box_with_array)
         tm.assert_equal(result, expected)
 
     # -------------------------------------------------------------
@@ -1034,16 +1022,13 @@ def test_dt64arr_naive_sub_dt64ndarray(self, box_with_array):
 
     def test_dt64arr_aware_sub_dt64ndarray_raises(self, tz_aware_fixture,
                                                   box_with_array):
-        if box_with_array is pd.DataFrame:
-            pytest.xfail("FIXME: ValueError with transpose; "
-                         "alignment error without")
 
         tz = tz_aware_fixture
         dti = pd.date_range('2016-01-01', periods=3, tz=tz)
         dt64vals = dti.values
 
         dtarr = tm.box_expected(dti, box_with_array)
-        msg = 'DatetimeArray subtraction must have the same timezones or'
+        msg = 'subtraction must have the same timezones or'
         with pytest.raises(TypeError, match=msg):
             dtarr - dt64vals
         with pytest.raises(TypeError, match=msg):
@@ -1054,9 +1039,6 @@ def test_dt64arr_aware_sub_dt64ndarray_raises(self, tz_aware_fixture,
 
     def test_dt64arr_add_dt64ndarray_raises(self, tz_naive_fixture,
                                             box_with_array):
-        if box_with_array is pd.DataFrame:
-            pytest.xfail("FIXME: ValueError with transpose; "
-                         "alignment error without")
 
         tz = tz_naive_fixture
         dti = pd.date_range('2016-01-01', periods=3, tz=tz)
@@ -1204,9 +1186,8 @@ def test_dti_add_tick_tzaware(self, tz_aware_fixture, box_with_array):
         expected = DatetimeIndex(['2010-11-01 05:00', '2010-11-01 06:00',
                                   '2010-11-01 07:00'], freq='H', tz=tz)
 
-        # FIXME: these raise ValueError with transpose=True
-        dates = tm.box_expected(dates, box_with_array, transpose=False)
-        expected = tm.box_expected(expected, box_with_array, transpose=False)
+        dates = tm.box_expected(dates, box_with_array)
+        expected = tm.box_expected(expected, box_with_array)
 
         # TODO: parametrize over the scalar being added?  radd?  sub?
         offset = dates + pd.offsets.Hour(5)
@@ -1359,26 +1340,25 @@ def test_dt64arr_add_sub_DateOffset(self, box_with_array):
 
         s = DatetimeIndex([Timestamp('2000-01-15 00:15:00', tz='US/Central'),
                            Timestamp('2000-02-15', tz='US/Central')], name='a')
-        # FIXME: ValueError with tzaware DataFrame transpose
-        s = tm.box_expected(s, box_with_array, transpose=False)
+        s = tm.box_expected(s, box_with_array)
         result = s + pd.offsets.Day()
         result2 = pd.offsets.Day() + s
         exp = DatetimeIndex([Timestamp('2000-01-16 00:15:00', tz='US/Central'),
                              Timestamp('2000-02-16', tz='US/Central')],
                             name='a')
-        exp = tm.box_expected(exp, box_with_array, transpose=False)
+        exp = tm.box_expected(exp, box_with_array)
         tm.assert_equal(result, exp)
         tm.assert_equal(result2, exp)
 
         s = DatetimeIndex([Timestamp('2000-01-15 00:15:00', tz='US/Central'),
                            Timestamp('2000-02-15', tz='US/Central')], name='a')
-        s = tm.box_expected(s, box_with_array, transpose=False)
+        s = tm.box_expected(s, box_with_array)
         result = s + pd.offsets.MonthEnd()
         result2 = pd.offsets.MonthEnd() + s
         exp = DatetimeIndex([Timestamp('2000-01-31 00:15:00', tz='US/Central'),
                              Timestamp('2000-02-29', tz='US/Central')],
                             name='a')
-        exp = tm.box_expected(exp, box_with_array, transpose=False)
+        exp = tm.box_expected(exp, box_with_array)
         tm.assert_equal(result, exp)
         tm.assert_equal(result2, exp)
 
@@ -1415,9 +1395,6 @@ def test_dt64arr_add_mixed_offset_array(self, box_with_array):
     def test_dt64arr_add_sub_offset_ndarray(self, tz_naive_fixture,
                                             box_with_array):
         # GH#18849
-        if box_with_array is pd.DataFrame:
-            pytest.xfail("FIXME: ValueError with transpose; "
-                         "alignment error without")
 
         tz = tz_naive_fixture
         dti = pd.date_range('2017-01-01', periods=2, tz=tz)
diff --git a/pandas/tests/arithmetic/test_timedelta64.py b/pandas/tests/arithmetic/test_timedelta64.py
index ead9876e7c2a8..3ab3f51b4e46a 100644
--- a/pandas/tests/arithmetic/test_timedelta64.py
+++ b/pandas/tests/arithmetic/test_timedelta64.py
@@ -857,10 +857,8 @@ def test_td64arr_add_timestamp(self, box_with_array, tz_naive_fixture):
         idx = TimedeltaIndex(['1 day', '2 day'])
         expected = DatetimeIndex(['2011-01-02', '2011-01-03'], tz=tz)
 
-        # FIXME: fails with transpose=True because of tz-aware DataFrame
-        #  transpose bug
-        idx = tm.box_expected(idx, box_with_array, transpose=False)
-        expected = tm.box_expected(expected, box_with_array, transpose=False)
+        idx = tm.box_expected(idx, box_with_array)
+        expected = tm.box_expected(expected, box_with_array)
 
         result = idx + other
         tm.assert_equal(result, expected)
diff --git a/pandas/tests/frame/test_constructors.py b/pandas/tests/frame/test_constructors.py
index 68017786eb6a6..c44698eab86f4 100644
--- a/pandas/tests/frame/test_constructors.py
+++ b/pandas/tests/frame/test_constructors.py
@@ -2399,3 +2399,32 @@ def test_nested_dict_construction(self):
             index=pd.Index([2001, 2002, 2003])
         )
         tm.assert_frame_equal(result, expected)
+
+    def test_from_tzaware_object_array(self):
+        # GH#26825 2D object array of tzaware timestamps should not raise
+        dti = pd.date_range('2016-04-05 04:30', periods=3, tz='UTC')
+        data = dti._data.astype(object).reshape(1, -1)
+        df = pd.DataFrame(data)
+        assert df.shape == (1, 3)
+        assert (df.dtypes == dti.dtype).all()
+        assert (df == dti).all().all()
+
+    def test_from_tzaware_mixed_object_array(self):
+        # GH#26825
+        arr = np.array([
+            [Timestamp('2013-01-01 00:00:00'),
+             Timestamp('2013-01-02 00:00:00'),
+             Timestamp('2013-01-03 00:00:00')],
+            [Timestamp('2013-01-01 00:00:00-0500', tz='US/Eastern'),
+             pd.NaT,
+             Timestamp('2013-01-03 00:00:00-0500', tz='US/Eastern')],
+            [Timestamp('2013-01-01 00:00:00+0100', tz='CET'),
+             pd.NaT,
+             Timestamp('2013-01-03 00:00:00+0100', tz='CET')]],
+            dtype=object).T
+        res = DataFrame(arr, columns=['A', 'B', 'C'])
+
+        expected_dtypes = ['datetime64[ns]',
+                           'datetime64[ns, US/Eastern]',
+                           'datetime64[ns, CET]']
+        assert (res.dtypes == expected_dtypes).all()
diff --git a/pandas/tests/frame/test_dtypes.py b/pandas/tests/frame/test_dtypes.py
index 96cf70483d4e7..7ed601e4f7046 100644
--- a/pandas/tests/frame/test_dtypes.py
+++ b/pandas/tests/frame/test_dtypes.py
@@ -978,9 +978,11 @@ def test_astype(self):
                               Timestamp('2013-01-03 00:00:00+0100',
                                         tz='CET')]],
                             dtype=object).T
+        expected = DataFrame(expected,
+                             index=self.tzframe.index,
+                             columns=self.tzframe.columns, dtype=object)
         result = self.tzframe.astype(object)
-        assert_frame_equal(result, DataFrame(
-            expected, index=self.tzframe.index, columns=self.tzframe.columns))
+        assert_frame_equal(result, expected)
 
         result = self.tzframe.astype('datetime64[ns]')
         expected = DataFrame({'A': date_range('20130101', periods=3),
diff --git a/pandas/tests/frame/test_operators.py b/pandas/tests/frame/test_operators.py
index f1c8445bf98e0..1e932879e9ad0 100644
--- a/pandas/tests/frame/test_operators.py
+++ b/pandas/tests/frame/test_operators.py
@@ -793,3 +793,44 @@ def test_no_warning(self, all_arithmetic_operators):
         b = df['B']
         with tm.assert_produces_warning(None):
             getattr(df, all_arithmetic_operators)(b, 0)
+
+
+class TestTranspose:
+    def test_transpose_tzaware_1col_single_tz(self):
+        # GH#26825
+        dti = pd.date_range('2016-04-05 04:30', periods=3, tz='UTC')
+
+        df = pd.DataFrame(dti)
+        assert (df.dtypes == dti.dtype).all()
+        res = df.T
+        assert (res.dtypes == dti.dtype).all()
+
+    def test_transpose_tzaware_2col_single_tz(self):
+        # GH#26825
+        dti = pd.date_range('2016-04-05 04:30', periods=3, tz='UTC')
+
+        df3 = pd.DataFrame({'A': dti, 'B': dti})
+        assert (df3.dtypes == dti.dtype).all()
+        res3 = df3.T
+        assert (res3.dtypes == dti.dtype).all()
+
+    def test_transpose_tzaware_2col_mixed_tz(self):
+        # GH#26825
+        dti = pd.date_range('2016-04-05 04:30', periods=3, tz='UTC')
+        dti2 = dti.tz_convert('US/Pacific')
+
+        df4 = pd.DataFrame({'A': dti, 'B': dti2})
+        assert (df4.dtypes == [dti.dtype, dti2.dtype]).all()
+        assert (df4.T.dtypes == object).all()
+        tm.assert_frame_equal(df4.T.T, df4)
+
+    def test_transpose_object_to_tzaware_mixed_tz(self):
+        # GH#26825
+        dti = pd.date_range('2016-04-05 04:30', periods=3, tz='UTC')
+        dti2 = dti.tz_convert('US/Pacific')
+
+        # mixed all-tzaware dtypes
+        df2 = pd.DataFrame([dti, dti2])
+        assert (df2.dtypes == object).all()
+        res2 = df2.T
+        assert (res2.dtypes == [dti.dtype, dti2.dtype]).all()
diff --git a/pandas/tests/groupby/test_function.py b/pandas/tests/groupby/test_function.py
index 3d9bfcd126377..14f27f0c4c7d8 100644
--- a/pandas/tests/groupby/test_function.py
+++ b/pandas/tests/groupby/test_function.py
@@ -963,12 +963,14 @@ def test_count():
 
     df['9th'] = df['9th'].astype('category')
 
-    for key in '1st', '2nd', ['1st', '2nd']:
+    for key in ['1st', '2nd', ['1st', '2nd']]:
         left = df.groupby(key).count()
         right = df.groupby(key).apply(DataFrame.count).drop(key, axis=1)
         tm.assert_frame_equal(left, right)
 
-    # GH5610
+
+def test_count_non_nulls():
+    # GH#5610
     # count counts non-nulls
     df = pd.DataFrame([[1, 2, 'foo'],
                        [1, np.nan, 'bar'],
diff --git a/pandas/tests/groupby/test_groupby.py b/pandas/tests/groupby/test_groupby.py
index 87b57b0609b36..dcd0d3938c6a5 100644
--- a/pandas/tests/groupby/test_groupby.py
+++ b/pandas/tests/groupby/test_groupby.py
@@ -129,12 +129,14 @@ def func(dataf):
     result = df.groupby('X', squeeze=False).count()
     assert isinstance(result, DataFrame)
 
+
+def test_inconsistent_return_type():
     # GH5592
-    # inconcistent return type
+    # inconsistent return type
     df = DataFrame(dict(A=['Tiger', 'Tiger', 'Tiger', 'Lamb', 'Lamb',
-                           'Pony', 'Pony'], B=Series(
-                               np.arange(7), dtype='int64'), C=date_range(
-                                   '20130101', periods=7)))
+                           'Pony', 'Pony'],
+                        B=Series(np.arange(7), dtype='int64'),
+                        C=date_range('20130101', periods=7)))
 
     def f(grp):
         return grp.iloc[0]


Code review comments:
diff_hunk: @@ -1664,3 +1655,36 @@ def _normalize_keyword_aggregation(kwargs):
         order.append((column,
                       com.get_callable_name(aggfunc) or aggfunc))
     return aggspec, columns, order
+
+
+def _recast_datetimelike_result(result):
path: pandas/core/groupby/generic.py
text: @user1:
can you add types for new code

@author:
sure

diff_hunk: @@ -1664,3 +1655,36 @@ def _normalize_keyword_aggregation(kwargs):
         order.append((column,
                       com.get_callable_name(aggfunc) or aggfunc))
     return aggspec, columns, order
+
+
+def _recast_datetimelike_result(result):
+    """
+    If we have date/time like in the original, then coerce dates
+    as we are stacking can easily have object dtypes here.
+
+    Parameters
+    ----------
+    result : DataFrame
+
+    Notes
+    -----
+    - Assumes Groupby._selected_obj has ndim==2 and at least one
+    datetimelike column
+    - Modifies `result` inplace
path: pandas/core/groupby/generic.py
text: @user1:
why is this inplace? that seems odd

@author:
Aside from not liking functions that do stuff inplace, why wouldn't it be?  It's (partially) inplace in the status quo.

@user1:
because we almost never like to do that unless its really upfront and in your face as its not obvious. try to use maybe_convert_objects anyhow

@author:
changed to copy and return instead of modifying inplace.  discussed maybe_convert_objects below

diff_hunk: @@ -1664,3 +1655,36 @@ def _normalize_keyword_aggregation(kwargs):
         order.append((column,
                       com.get_callable_name(aggfunc) or aggfunc))
     return aggspec, columns, order
+
+
+def _recast_datetimelike_result(result):
path: pandas/core/groupby/generic.py
text: @user1:
so i would put this in pandas.core.dtypes.cast, our dumping ground for casting, right after / before maybe_infer_to-datetimelike (and you can de-privatize)


@author:
This is really kludgy code (and is replacing equally kludgy code; kind of like turtles all the way down).  I'd rather keep it close to its only usage and hope it is ripped out by someone who knows this part of the code better

@user1:
as i said this look a *lot* like maybe_convert_objects, try to use that here

@author:
Note also: we only have 5 tests that go through this path

diff_hunk: @@ -1664,3 +1655,36 @@ def _normalize_keyword_aggregation(kwargs):
         order.append((column,
                       com.get_callable_name(aggfunc) or aggfunc))
     return aggspec, columns, order
+
+
+def _recast_datetimelike_result(result):
+    """
+    If we have date/time like in the original, then coerce dates
+    as we are stacking can easily have object dtypes here.
+
+    Parameters
+    ----------
+    result : DataFrame
+
+    Notes
+    -----
+    - Assumes Groupby._selected_obj has ndim==2 and at least one
+    datetimelike column
+    - Modifies `result` inplace
+    """
+    ocols = [idx for idx in range(len(result.columns))
+             if result.dtypes[idx] == object]
+
+    for cidx in ocols:
path: pandas/core/groupby/generic.py
text: @user1:
this routines feels almost like maybe_convert_objects

@author:
Ha yep, in the next line there's a comment "# TODO: get maybe_convert_objects working here".  I tried this and got shiny new failures bc maybe_convert_objects expects a 1D ndarray.

@user1:
maybe_convert_objects is only 1d, my point is to *use* it here

@author:
When I do this, I get one test failure, but it looks like it may be another test-is-wrong thing, need confirmation.

tests.groupby.test_apply.test_apply_numeric_coercion_when_datetime

```
df = pd.DataFrame({'Number': [1, 2],
                           'Date': ["2017-03-02"] * 2,
                           'Str': ["foo", "inf"]})

        expected = df.groupby(['Number']).apply(lambda x: x.iloc[0])
        df.Date = pd.to_datetime(df.Date)
        result = df.groupby(['Number']).apply(lambda x: x.iloc[0])
        tm.assert_series_equal(result['Str'], expected['Str'])
```

In master, `result['Date']` has object-dtype.  If I use maybe_convert_objects here, it has datetime64[ns] dtype.  Should it be object dtype?

@user2:
Can you double-check? On master I have `result.dtypes`


```python
In [10]: result.dtypes
Out[10]:
Number             int64
Date      datetime64[ns]
Str               object
dtype: object
```

@author:
Good catch, I had this one wrong in a couple of different directions.