Repository: pandas-dev/pandas

Issue:
Make Series.shift always a copy?
Right now, `Series.shift(0)` will just return the series. Shifting for all other periods induces a copy:

```python
In [1]: import pandas as pd

In [2]: a = pd.Series([1, 2])

In [3]: a.shift(1) is a
Out[3]: False

In [4]: a.shift(0) is a
Out[4]: True
```

Should we defensively copy on `0` as well, for a consistent user experience?

https://github.com/pandas-dev/pandas/blob/e669fae0762d901e61f7af84fc3b5181848d257d/pandas/core/generic.py#L8084-L8086
np.ndarray[object] - Timedelta raises
```
arr = np.array([pd.Timestamp.now(), pd.Timedelta('2D')])

>>> arr - pd.Timedelta('1D')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for -: 'numpy.ndarray' and 'Timedelta'
```

It should attempt to operate element-wise.


Discussion:

I suppose this is because of 

https://github.com/pandas-dev/pandas/blob/27ebb3e1e40513ad5f8919a5bbc7298e2e070a39/pandas/_libs/tslibs/timedeltas.pyx#L539-L544

Any idea what the "wrong" answer would be? (with timedelta.timedelta instead of Timedelta that seems to work just fine, so I assume with Timedelta it will be the same)
No idea what the wrong answer would be.  This should be easy to fix; if no one else picks it up I'll take care of it once the current PR queue settles down.
Yes, PR with a fix is certainly welcome I think

Resolving patch:
diff --git a/pandas/core/frame.py b/pandas/core/frame.py
--- a/pandas/core/frame.py
+++ b/pandas/core/frame.py
@@ -3273,7 +3273,7 @@ def assign(self, **kwargs):
 
         Parameters
         ----------
-        kwargs : keyword, value pairs
+        **kwargs : dict of {str: callable or Series}
             The column names are keywords. If the values are
             callable, they are computed on the DataFrame and
             assigned to the new columns. The callable must not
@@ -3283,7 +3283,7 @@ def assign(self, **kwargs):
 
         Returns
         -------
-        df : DataFrame
+        DataFrame
             A new DataFrame with the new columns in addition to
             all the existing columns.
 
@@ -3303,48 +3303,34 @@ def assign(self, **kwargs):
 
         Examples
         --------
-        >>> df = pd.DataFrame({'A': range(1, 11), 'B': np.random.randn(10)})
+        >>> df = pd.DataFrame({'temp_c': [17.0, 25.0]},
+        ...                   index=['Portland', 'Berkeley'])
+        >>> df
+                  temp_c
+        Portland    17.0
+        Berkeley    25.0
 
         Where the value is a callable, evaluated on `df`:
-
-        >>> df.assign(ln_A = lambda x: np.log(x.A))
-            A         B      ln_A
-        0   1  0.426905  0.000000
-        1   2 -0.780949  0.693147
-        2   3 -0.418711  1.098612
-        3   4 -0.269708  1.386294
-        4   5 -0.274002  1.609438
-        5   6 -0.500792  1.791759
-        6   7  1.649697  1.945910
-        7   8 -1.495604  2.079442
-        8   9  0.549296  2.197225
-        9  10 -0.758542  2.302585
-
-        Where the value already exists and is inserted:
-
-        >>> newcol = np.log(df['A'])
-        >>> df.assign(ln_A=newcol)
-            A         B      ln_A
-        0   1  0.426905  0.000000
-        1   2 -0.780949  0.693147
-        2   3 -0.418711  1.098612
-        3   4 -0.269708  1.386294
-        4   5 -0.274002  1.609438
-        5   6 -0.500792  1.791759
-        6   7  1.649697  1.945910
-        7   8 -1.495604  2.079442
-        8   9  0.549296  2.197225
-        9  10 -0.758542  2.302585
-
-        Where the keyword arguments depend on each other
-
-        >>> df = pd.DataFrame({'A': [1, 2, 3]})
-
-        >>> df.assign(B=df.A, C=lambda x:x['A']+ x['B'])
-            A  B  C
-         0  1  1  2
-         1  2  2  4
-         2  3  3  6
+        >>> df.assign(temp_f=lambda x: x.temp_c * 9 / 5 + 32)
+                  temp_c  temp_f
+        Portland    17.0    62.6
+        Berkeley    25.0    77.0
+
+        Alternatively, the same behavior can be achieved by directly
+        referencing an existing Series or sequence:
+        >>> df.assign(temp_f=df['temp_c'] * 9 / 5 + 32)
+                  temp_c  temp_f
+        Portland    17.0    62.6
+        Berkeley    25.0    77.0
+
+        In Python 3.6+, you can create multiple columns within the same assign
+        where one of the columns depends on another one defined within the same
+        assign:
+        >>> df.assign(temp_f=lambda x: x['temp_c'] * 9 / 5 + 32,
+        ...           temp_k=lambda x: (x['temp_f'] +  459.67) * 5 / 9)
+                  temp_c  temp_f  temp_k
+        Portland    17.0    62.6  290.15
+        Berkeley    25.0    77.0  298.15
         """
         data = self.copy()
 


Test patch: