Repository: pandas-dev/pandas

Issue:
Inconsistent return type for downsampling on resample of empty DataFrame
#### Code Sample, a copy-pastable example if possible

```python
>>> df1 = pd.DataFrame(dict(a=range(100)), index=pd.date_range('1/1/2000', periods=100, freq="M"))
>>> df2 = df1[df1.a < 0]
>>> df1.shape
(100, 1)
>>> df2.shape
(0, 1)
>>> df2.empty
True
>>> type(df1.resample("Q").size())
<class 'pandas.core.series.Series'>
>>> type(df2.resample("Q").size())
<class 'pandas.core.frame.DataFrame'>
```
#### Problem description

Code that is resampling a DataFrame that also has some filtering should be able to expect consistent types.  My specific case of this causing a problem was that the `size()` output was being forwarded on to other code using `Series.tolist()`, which obviously fails when the output is a DataFrame.

#### Expected Output

Expectation is that `.size()` should always return a `Series`.

#### Output of ``pd.show_versions()``

<details>

INSTALLED VERSIONS
------------------
commit: None
python: 3.5.2.final.0
python-bits: 64
OS: Linux
OS-release: 3.16.0-4-amd64
machine: x86_64
processor: 
byteorder: little
LC_ALL: None
LANG: en_CA.utf8
LOCALE: en_CA.UTF-8

pandas: 0.19.1
nose: None
pip: 9.0.1
setuptools: 27.2.0
Cython: None
numpy: 1.11.2
scipy: 0.18.1
statsmodels: None
xarray: None
IPython: None
sphinx: None
patsy: None
dateutil: 2.6.0
pytz: 2016.7
blosc: None
bottleneck: None
tables: None
numexpr: 2.6.1
matplotlib: 1.5.3
openpyxl: None
xlrd: None
xlwt: None
xlsxwriter: None
lxml: None
bs4: None
html5lib: None
httplib2: None
apiclient: None
sqlalchemy: None
pymysql: None
psycopg2: None
jinja2: None
boto: None
pandas_datareader: None
</details>



Discussion:
we do this correctly for groupby, so will mark this as a bug.
pull requests welcome!

```
In [9]: df = pd.DataFrame({'A' : [1,1,2],'B':[1,2,3]})

In [10]: df
Out[10]: 
   A  B
0  1  1
1  1  2
2  2  3

In [11]: df.groupby('A').size()
Out[11]: 
A
1    2
2    1
dtype: int64

In [12]: df[df.A>3].groupby('A').size()
Out[12]: Series([], dtype: int64)

```
@jreback I am interested in working on the issue. How do I start?
@souravsingh I followed [this guide](http://pandas.pydata.org/pandas-docs/stable/contributing.html) provided by the project website.
I'm a novice and have traced the DateTimeIndexResampler as follows -
DateTimeIndexResampler->Resampler-> _Groupby->(pandasobject,selectionMixin)

I couldn't identify the size method , besides the one mentioned in groupby class . Help regarding the same would be welcome.
Search for `size` in the tseries/resample.py. You will see that it is added to the Resampler object using `_downsample(method='size')`
@jreback @rwarren @jorisvandenbossche  is it exactly a bug? There is a test `test_resample_empty_dataframe` ( `pandas/tseries/tests/test_resample.py`) where checks that the input empty dataframe should equal the result empty dataframe after resampling.

```
def test_resample_empty_dataframe(self):
        # GH13212
        index = self.create_series().index[:0]
        f = DataFrame(index=index)

        for freq in ['M', 'D', 'H']:
            # count retains dimensions too
            methods = downsample_methods + ['count']
            for method in methods:
                result = getattr(f.resample(freq), method)()

                expected = f.copy()
                expected.index = f.index._shallow_copy(freq=freq)
                assert_index_equal(result.index, expected.index)
                self.assertEqual(result.index.freq, expected.index.freq)
                assert_frame_equal(result, expected, check_dtype=False)
```
``size`` is not in the downsample_methods :<, so this should be

``methods = downsample_methods + upsample_methods`` (nunique needs to be excluded / separately tested as its series only)

Resolving patch:
diff --git a/pandas/core/resample.py b/pandas/core/resample.py
--- a/pandas/core/resample.py
+++ b/pandas/core/resample.py
@@ -17,6 +17,7 @@
 from pandas.core.indexes.period import PeriodIndex, period_range
 import pandas.core.common as com
 import pandas.core.algorithms as algos
+from pandas.core.dtypes.generic import ABCDataFrame
 
 import pandas.compat as compat
 from pandas.compat.numpy import function as nv
@@ -549,6 +550,15 @@ def var(self, ddof=1, *args, **kwargs):
         nv.validate_resampler_func('var', args, kwargs)
         return self._downsample('var', ddof=ddof)
 
+    @Appender(GroupBy.size.__doc__)
+    def size(self):
+        # It's a special case as higher level does return
+        # a copy of 0-len objects. GH14962
+        result = self._downsample('size')
+        if not len(self.ax) and isinstance(self._selected_obj, ABCDataFrame):
+            result = pd.Series([], index=result.index, dtype='int64')
+        return result
+
 
 Resampler._deprecated_valids += dir(Resampler)
 
@@ -563,8 +573,7 @@ def f(self, _method=method, *args, **kwargs):
     setattr(Resampler, method, f)
 
 # groupby & aggregate methods
-for method in ['count', 'size']:
-
+for method in ['count']:
     def f(self, _method=method):
         return self._downsample(_method)
     f.__doc__ = getattr(GroupBy, method).__doc__


Test patch:
diff --git a/pandas/tests/test_resample.py b/pandas/tests/test_resample.py
--- a/pandas/tests/test_resample.py
+++ b/pandas/tests/test_resample.py
@@ -783,15 +783,19 @@ def test_resample_empty_dataframe(self):
 
         for freq in ['M', 'D', 'H']:
             # count retains dimensions too
-            methods = downsample_methods + ['count']
+            methods = downsample_methods + upsample_methods
             for method in methods:
                 result = getattr(f.resample(freq), method)()
+                if method != 'size':
+                    expected = f.copy()
+                else:
+                    # GH14962
+                    expected = Series([])
 
-                expected = f.copy()
                 expected.index = f.index._shallow_copy(freq=freq)
                 assert_index_equal(result.index, expected.index)
                 assert result.index.freq == expected.index.freq
-                assert_frame_equal(result, expected, check_dtype=False)
+                assert_almost_equal(result, expected, check_dtype=False)
 
             # test size for GH13212 (currently stays as df)