Repository: pandas-dev/pandas

Issue:
DataFrame corrupted after improper column creation
I want to add a column to DataFrame using a list of arrays, but I forgot to change the 2d array into list first, so  I got a "Wrong number of items passed" error.

After that, the df seemed to be corrupted, whenever I want to print it, it kept raising a "NoneType object is not iterable error".
While it could still be added new columns, the improperly assigned columns couldn't be deleted yet (also prompting the NoneType error).

This the code that could reproduce my problem:

``` python
df = DataFrame(np.ones((4,4)))
df['foo'] = np.ones((4,2)).tolist()  # OK, and that's what I should type
df['test'] = np.ones((4,2))  # the improper column creation
AsssertionError: Wrong number of items passed （1 vs 2)

df
TypeError: 'NoneType' object is not iterable, u'occured at index foo'

del df['test']
TypeError: 'NoneType' object is not iterable
```



Discussion:
You realize you are creating an embedded list (what you are assigning to `df['foo']`
Is there a reason you are trying to put a 2-d array where you would normally put a 1-d?

```
In [15]: pd.Series(np.ones((4,2)).tolist())
Out[15]: 
0    [1.0, 1.0]
1    [1.0, 1.0]
2    [1.0, 1.0]
3    [1.0, 1.0]
dtype: object
```

Yes, you're right, I know your way does work and I should do that way. My point is that I wrongly assigned the column, and then realized that I made a mistake. When I went on my work, DataFrame corrupted. I used to thought  that DataFrame would be robust to my mistake.

``` python
In [1]:  df._data.items
Out[1]:  [0,1,2,3,'foo', 'test']

loc = df._data.get_loc('test')
new_items = df._data.items.delete(loc)
df._data.set_items_norename(new_items)
```

Above lines could finally fix my problem (after refering to the source code in core/internals.py ). The problem is due to that the data block corresponding to `df['test']` was empty.

But I still wonder why DataFrame add the item `df['test']` into its items list, while the data block hasn't been added properly?


Resolving patch:
diff --git a/pandas/core/internals.py b/pandas/core/internals.py
--- a/pandas/core/internals.py
+++ b/pandas/core/internals.py
@@ -1334,11 +1334,22 @@ def insert(self, loc, item, value):
         if item in self.items:
             raise Exception('cannot insert %s, already exists' % item)
 
-        new_items = self.items.insert(loc, item)
-        self.set_items_norename(new_items)
+        try:
+            new_items = self.items.insert(loc, item)
+            self.set_items_norename(new_items)
+
+            # new block
+            self._add_new_block(item, value, loc=loc)
 
-        # new block
-        self._add_new_block(item, value, loc=loc)
+        except:
+
+            # so our insertion operation failed, so back out of the new items
+            # GH 3010
+            new_items = self.items.delete(loc)
+            self.set_items_norename(new_items)
+            
+            # re-raise
+            raise
 
         if len(self.blocks) > 100:
             self._consolidate_inplace()


Test patch:
diff --git a/pandas/tests/test_frame.py b/pandas/tests/test_frame.py
--- a/pandas/tests/test_frame.py
+++ b/pandas/tests/test_frame.py
@@ -1968,6 +1968,19 @@ def test_constructor_cast_failure(self):
         foo = DataFrame({'a': ['a', 'b', 'c']}, dtype=np.float64)
         self.assert_(foo['a'].dtype == object)
 
+        # GH 3010, constructing with odd arrays
+        df = DataFrame(np.ones((4,2)))
+   
+        # this is ok
+        df['foo'] = np.ones((4,2)).tolist()
+
+        # this is not ok
+        self.assertRaises(AssertionError, df.__setitem__, tuple(['test']), np.ones((4,2)))
+
+        # this is ok
+        df['foo2'] = np.ones((4,2)).tolist()
+
+
     def test_constructor_dtype_nocast_view(self):
         df = DataFrame([[1, 2]])
         should_be_view = DataFrame(df, dtype=df[0].dtype)