Repository: pandas-dev/pandas

Issue:
Setting empty column on empty DataFrame
This issue appears in 0.13.0-rc1 and did not appear in 0.12.0-854-gde63e00

Let us say we have an empty DataFrame. Then the following calls (which worked in pandas 0.12) no longer work:

``` python
df = pd.DataFrame()
df['foo'] = []

df = pd.DataFrame()
df['foo'] = df.index

df = pd.DataFrame()
df['foo'] = range(len(df))
```

They all throw an exception:

```
File "/usr/local/lib/python3.2/dist-packages/pandas-0.13.0rc1-py3.2-linux-x86_64.egg/pandas/core/frame.py", line 1855, in __setitem__
    self._set_item(key, value)
  File "/usr/local/lib/python3.2/dist-packages/pandas-0.13.0rc1-py3.2-linux-x86_64.egg/pandas/core/frame.py", line 1915, in _set_item
    self._ensure_valid_index(value)

File "/usr/local/lib/python3.2/dist-packages/pandas-0.13.0rc1-py3.2-linux-x86_64.egg/pandas/core/frame.py", line 1899, in _ensure_valid_index
    raise ValueError('Cannot set a frame with no defined index '
ValueError: Cannot set a frame with no defined index and a non-series
```

The following will work:

``` python
df = pd.DataFrame()
df['foo'] = pd.Series([])

df = pd.DataFrame()
df['foo'] = pd.Series(df.index)

df = pd.DataFrame()
df['foo'] = pd.Series(range(len(df)))
```

The issue appears to be on lines 1897-1899 of pandas.core.frame:

``` python
if not len(self.index):
    if not isinstance(value, Series):
        raise ValueError('Cannot set a frame with no defined index '
```

Perhaps it could be changed to:

``` python
if not len(self.index) and len(value) > 0:
    if not isinstance(value, Series):
        raise ValueError('Cannot set a frame with no defined index '
```



Discussion:
What is an actual usecase of doing this?

I stumbled upon it because I do a merge and then create a new column which is a copy of the index (for further manipulation).  Sometimes that merge leaves me with an empty DataFrame.

More generally, I might want to create an auto-numbered column with `df['foo'] = range(len(df))`.  With the recent code change, I either have to change that to `df['foo'] = pd.Series(range(len(df)))` or do a check to ensure that df is not empty.

you don't need to auto number
the index if you don't set it IS an auto numbered index (and if u have a different index, you can just df.reset_index())
no need to actually have a numbering


Resolving patch:
diff --git a/pandas/core/frame.py b/pandas/core/frame.py
--- a/pandas/core/frame.py
+++ b/pandas/core/frame.py
@@ -1895,9 +1895,16 @@ def _ensure_valid_index(self, value):
         passed value
         """
         if not len(self.index):
+
+            # GH5632, make sure that we are a Series convertible
+            try:
+                value = Series(value)
+            except:
+                pass
+
             if not isinstance(value, Series):
                 raise ValueError('Cannot set a frame with no defined index '
-                                 'and a non-series')
+                                 'and a value that cannot be converted to a Series')
             self._data.set_axis(1, value.index.copy(), check_axis=False)
 
     def _set_item(self, key, value):


Test patch:
diff --git a/pandas/tests/test_indexing.py b/pandas/tests/test_indexing.py
--- a/pandas/tests/test_indexing.py
+++ b/pandas/tests/test_indexing.py
@@ -1635,6 +1635,42 @@ def f():
             df.loc[:,1] = 1
         self.assertRaises(ValueError, f)
 
+        # these work as they don't really change
+        # anything but the index
+        # GH5632
+        expected = DataFrame(columns=['foo'])
+        def f():
+            df = DataFrame()
+            df['foo'] = Series([])
+            return df
+        assert_frame_equal(f(), expected)
+        def f():
+            df = DataFrame()
+            df['foo'] = Series(df.index)
+            return df
+        assert_frame_equal(f(), expected)
+        def f():
+            df = DataFrame()
+            df['foo'] = Series(range(len(df)))
+            return df
+        assert_frame_equal(f(), expected)
+        def f():
+            df = DataFrame()
+            df['foo'] = []
+            return df
+        assert_frame_equal(f(), expected)
+        def f():
+            df = DataFrame()
+            df['foo'] = df.index
+            return df
+        assert_frame_equal(f(), expected)
+        def f():
+            df = DataFrame()
+            df['foo'] = range(len(df))
+            return df
+        assert_frame_equal(f(), expected)
+
+        df = DataFrame()
         df2 = DataFrame()
         df2[1] = Series([1],index=['foo'])
         df.loc[:,1] = Series([1],index=['foo'])