Repository: pandas-dev/pandas

Issue:
API/BUG: should df.loc[:,'col'] be the same as df['col'] for assignment
see also: http://stackoverflow.com/questions/21415432/pandas-v0-13-0-setting-dataframe-values-of-type-datetime64ns

~~I believe this changed from 0.12.~~ was the same in 0.12

Boils down to if the row indexer is a null-slice (IOW all rows are selected), should
dtype conversion be done or not (as it current)

```
In [45]: df = pd.DataFrame({'date':pd.date_range('2000-01-01','2000-01-5'),'val' : np.arange(5)})

In [46]: df
Out[46]: 
        date  val
0 2000-01-01    0
1 2000-01-02    1
2 2000-01-03    2
3 2000-01-04    3
4 2000-01-05    4

[5 rows x 2 columns]

In [47]: df['date'] = 0

In [48]: df
Out[48]: 
   date  val
0     0    0
1     0    1
2     0    2
3     0    3
4     0    4

[5 rows x 2 columns]

In [49]: df = pd.DataFrame({'date':pd.date_range('2000-01-01','2000-01-5'),'val' : np.arange(5)})

In [53]: df.loc[:,'date'] = np.array([0])

In [54]: df
Out[54]: 
        date  val
0 1970-01-01    0
1 1970-01-01    1
2 1970-01-01    2
3 1970-01-01    3
4 1970-01-01    4

[5 rows x 2 columns]
```



Discussion:
Tough one.

was going to throw this in the docs and then think about this for 0.14
yes?

```
+.. _indexing.getitem_vs_loc:
+
+``[]`` vs loc
+-------------
+
+``[]`` can behave slightly differently versus a similar ``.loc``
+expression for setitem. An example will help explain what is occuring.
+
+.. ipython:: python
+
+   t  = date_range('2000-01-01','2000-01-05')
+   v  = np.arange(0,len(t))
+   df = DataFrame({'date':t,'val':v})
+   df
+   df.dtypes
+
+Setting via ``[]`` overwrites **the entire column**, so the dtype of the resultant column
+will be the same as the provided value.
+
+.. ipython:: python
+
+   df['date'] = v[0]
+   df.dtypes
+
+On the other hand, ``df.loc[:,'date']`` says something different. It says, overwrite the
+current data with the value that is provided to be set, but don't change the dtype, even
+though its slicing the entire column. This is done to avoid having to check for dtype changes
+each time on this particular setting type of operation.
+
+.. ipython:: python
+
+   df = DataFrame({'date':t,'val':v})
+   df.loc[:,'date'] = v[0]
+   df
+   df.dtypes
+
```

hmm. That's a corner case. I think that part of the docs is there to help
the user understand how to use pandas. That snippet answers a question I don't think
99.999% of users will be asking themselves while reading the docs and is 
therefore out of place.

perhaps a SO question with accepted answer and a GH issue are  good enough 
to cover this? Sometimes more is less.

ok...going to move this to 0.14 to think about it

my 2c, when you assign an entire column, the resulting dtype should match what
would have been there if you passed in the same (broadcasted in this case)
column data at construction time. There may be details I'm unaware of,
but that's what i'd intuitively expect.

ok...i can change pretty easily ....maybe will do tomorrow..


Resolving patch:
diff --git a/pandas/core/indexing.py b/pandas/core/indexing.py
--- a/pandas/core/indexing.py
+++ b/pandas/core/indexing.py
@@ -394,10 +394,18 @@ def setter(item, v):
                 s = self.obj[item]
                 pi = plane_indexer[0] if lplane_indexer == 1 else plane_indexer
 
-                # set the item, possibly having a dtype change
-                s = s.copy()
-                s._data = s._data.setitem(indexer=pi, value=v)
-                s._maybe_update_cacher(clear=True)
+                # perform the equivalent of a setitem on the info axis
+                # as we have a null slice which means essentially reassign to the columns
+                # of a multi-dim object
+                # GH6149
+                if isinstance(pi, tuple) and all(_is_null_slice(idx) for idx in pi):
+                    s = v
+                else:
+                    # set the item, possibly having a dtype change
+                    s = s.copy()
+                    s._data = s._data.setitem(indexer=pi, value=v)
+                    s._maybe_update_cacher(clear=True)
+
                 self.obj[item] = s
 
             def can_do_equal_len():


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
@@ -690,6 +690,52 @@ def test_loc_general(self):
         assert_series_equal(result,expected)
         self.assertEqual(result.dtype, object)
 
+    def test_loc_setitem_consistency(self):
+
+        # GH 6149
+        # coerce similary for setitem and loc when rows have a null-slice
+        expected = DataFrame({ 'date': Series(0,index=range(5),dtype=np.int64),
+                               'val' : Series(range(5),dtype=np.int64) })
+
+        df = DataFrame({ 'date': date_range('2000-01-01','2000-01-5'),
+                         'val' : Series(range(5),dtype=np.int64) })
+        df.loc[:,'date'] = 0
+        assert_frame_equal(df,expected)
+
+        df = DataFrame({ 'date': date_range('2000-01-01','2000-01-5'),
+                         'val' : Series(range(5),dtype=np.int64) })
+        df.loc[:,'date'] = np.array(0,dtype=np.int64)
+        assert_frame_equal(df,expected)
+
+        df = DataFrame({ 'date': date_range('2000-01-01','2000-01-5'),
+                         'val' : Series(range(5),dtype=np.int64) })
+        df.loc[:,'date'] = np.array([0,0,0,0,0],dtype=np.int64)
+        assert_frame_equal(df,expected)
+
+        expected = DataFrame({ 'date': Series('foo',index=range(5)),
+                               'val' : Series(range(5),dtype=np.int64) })
+        df = DataFrame({ 'date': date_range('2000-01-01','2000-01-5'),
+                         'val' : Series(range(5),dtype=np.int64) })
+        df.loc[:,'date'] = 'foo'
+        assert_frame_equal(df,expected)
+
+        expected = DataFrame({ 'date': Series(1.0,index=range(5)),
+                               'val' : Series(range(5),dtype=np.int64) })
+        df = DataFrame({ 'date': date_range('2000-01-01','2000-01-5'),
+                         'val' : Series(range(5),dtype=np.int64) })
+        df.loc[:,'date'] = 1.0
+        assert_frame_equal(df,expected)
+
+        # empty (essentially noops)
+        expected = DataFrame(columns=['x', 'y'])
+        df = DataFrame(columns=['x', 'y'])
+        df.loc[:, 'x'] = 1
+        assert_frame_equal(df,expected)
+
+        df = DataFrame(columns=['x', 'y'])
+        df['x'] = 1
+        assert_frame_equal(df,expected)
+
     def test_loc_setitem_frame(self):
         df = self.frame_labels