Repository: pandas-dev/pandas
Pull request: TST: tests for inconsistent indexing with datetimes

- [X] closes #9336
- [X] tests added / passed
- [X] passes `git diff upstream/master -u -- "*.py" | flake8 --diff`
- [ ] whatsnew entry

Problem statement:
Problem mixing datetime64 with other types in DataFrame column
Given a DataFrame with a column of type  datetime64, there is a problem assigning values of other types to slice of said column:
    in [148]:

```
import datetime as dt
import pandas as pd
import numpy as np
df = pd.DataFrame(np.random.random((3, 2)), columns = ['a', 'b' ])
df['c'] = pd.NaT
df.c

Out[148]:
0   NaT
1   NaT
2   NaT
Name: c, dtype: datetime64[ns]
```

It doesn't mind this:

```
In[185]:df.loc[0, 'c'] = pd.datetime.today()
df.c
Out[185]: 
0   2015-01-22 10:56:49.763999
1                          NaT
2                          NaT
```

Now try assigning values of a another type:

```
In [186]: df.loc[0, 'c'] = 1
AttributeError: 'int' object has no attribute 'view'

In[187]: df.loc[[0, 1], 'c'] = 'a', 'b'
AttributeError: 'tuple' object has no attribute 'view'
```

Now, the problem runs deeper than this. I didn't actually come across it when trying to assign mixed types to a single column. I was actually assigning values to a block of a DataFrame which included the time column. Here is an example:

```
In [252]: 
A = pd.DataFrame([[2, pd.datetime(2013, 1, 1)], [3, pd.datetime(2014, 1, 1)]])

In[253]: 
df.loc[[0, 1], ['a', 'c']] = A.values
AttributeError: 'list' object has no attribute 'view'
```

Note that the following works:

```
 In[254]: 
 df.loc[[0, 1], ['a', 'c']] = A
```

So when using the array A.values, the dtype is forced to object which then causes the problem. However, in the case where A has a different index than df, using A.values is necessary to assign the values. Maybe I need to be more careful but the error message doesn't help and it took me a long time to understand the problem. If I try to assign values to a slice of column 'c' using the corresponding slice of A.values I get a different error, which is more informative:

```
In[255]: 
df.loc[[0, 1],  'c'] = A.values[:, 1]
TypeError: Cannot change data-type for object array.
```

Hints from issue discussion:
these could certainly have a trapped (and then better message).

Usually we allow things like this but coerce to `object`. In general though I think raising is better.
If you really really want say a mixed-dtype Series then one should construct it directly.

Welcome a pull-request to update the errors.

this is fixed in master if anyone would like to put up some tests.

Commit message: TST: rest of the examples in #9336 added to new testing method

Patch to review:
diff --git a/pandas/tests/frame/test_indexing.py b/pandas/tests/frame/test_indexing.py
index 6d74ce54faa94..91b4b35f70b21 100644
--- a/pandas/tests/frame/test_indexing.py
+++ b/pandas/tests/frame/test_indexing.py
@@ -1548,6 +1548,25 @@ def test_setitem_single_column_mixed_datetime(self):
         # pytest.raises(
         #    Exception, df.loc.__setitem__, ('d', 'timestamp'), [nan])
 
+    def test_setitem_mixed_datetime(self):
+        # GH 9336
+        expected = DataFrame({'a': [0, 0, 0, 0, 13, 14],
+                              'b': [pd.datetime(2012, 1, 1),
+                                    1,
+                                    'x',
+                                    'y',
+                                    pd.datetime(2013, 1, 1),
+                                    pd.datetime(2014, 1, 1)]})
+        df = pd.DataFrame(np.zeros((6, 2), dtype=int), columns=['a', 'b'])
+        df['b'] = pd.NaT
+        df.loc[0, 'b'] = pd.datetime(2012, 1, 1)
+        df.loc[1, 'b'] = 1
+        df.loc[[2, 3], 'b'] = 'x', 'y'
+        A = pd.DataFrame([[13, pd.datetime(2013, 1, 1)],
+                          [14, pd.datetime(2014, 1, 1)]])
+        df.loc[[4, 5], ['a', 'b']] = A.values
+        assert_frame_equal(df, expected)
+
     def test_setitem_frame(self):
         piece = self.frame.loc[self.frame.index[:2], ['A', 'B']]
         self.frame.loc[self.frame.index[-2]:, ['A', 'B']] = piece.values


Code review comments:
diff_hunk: @@ -1548,6 +1548,25 @@ def test_setitem_single_column_mixed_datetime(self):
         # pytest.raises(
         #    Exception, df.loc.__setitem__, ('d', 'timestamp'), [nan])
 
+    def test_setitem_mixed_datetime(self):
+        # GH 9336
+        expected = DataFrame({'a': [0, 0, 0, 0, 13, 14],
+                              'b': [pd.datetime(2012, 1, 1),
+                                    1,
+                                    'x',
+                                    'y',
+                                    pd.datetime(2013, 1, 1),
+                                    pd.datetime(2014, 1, 1)]})
+        df = pd.DataFrame(np.zeros((6, 2), dtype=int), columns=['a', 'b'])
path: pandas/tests/frame/test_indexing.py
text: @user1:
this is failing because you are specify the dtype directly I think, just use 
``pd.DataFrame(0, columns = list('ab'), index=range(6))``

diff_hunk: @@ -1548,6 +1548,25 @@ def test_setitem_single_column_mixed_datetime(self):
         # pytest.raises(
         #    Exception, df.loc.__setitem__, ('d', 'timestamp'), [nan])
 
+    def test_setitem_mixed_datetime(self):
+        # GH 9336
+        expected = DataFrame({'a': [0, 0, 0, 0, 13, 14],
+                              'b': [pd.datetime(2012, 1, 1),
+                                    1,
+                                    'x',
+                                    'y',
+                                    pd.datetime(2013, 1, 1),
+                                    pd.datetime(2014, 1, 1)]})
+        df = pd.DataFrame(np.zeros((6, 2), dtype=int), columns=['a', 'b'])
+        df['b'] = pd.NaT
+        df.loc[0, 'b'] = pd.datetime(2012, 1, 1)
+        df.loc[1, 'b'] = 1
+        df.loc[[2, 3], 'b'] = 'x', 'y'
+        A = pd.DataFrame([[13, pd.datetime(2013, 1, 1)],
+                          [14, pd.datetime(2014, 1, 1)]])
+        df.loc[[4, 5], ['a', 'b']] = A.values
path: pandas/tests/frame/test_indexing.py
text: @user1:
can you assign with a numpy array that you construct directly